URI:
       twritegif.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
       ---
       twritegif.c (10728B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <draw.h>
            4 #include <memdraw.h>
            5 #include <bio.h>
            6 #include "imagefile.h"
            7 
            8 enum
            9 {
           10         Nhash        = 4001,
           11         Nbuf                = 300
           12 };
           13 
           14 typedef struct Entry Entry;
           15 typedef struct IO IO;
           16 
           17 
           18 struct Entry
           19 {
           20         int                index;
           21         int                prefix;
           22         int                exten;
           23         Entry        *next;
           24 };
           25 
           26 struct IO
           27 {
           28         Biobuf        *fd;
           29         uchar        buf[Nbuf];
           30         int                i;
           31         int                nbits;        /* bits in right side of shift register */
           32         int                sreg;                /* shift register */
           33 };
           34 
           35 static Rectangle        mainrect;
           36 static Entry        tbl[4096];
           37 static uchar        *colormap[5];        /* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */
           38 #define        GREYMAP        4
           39 static int                colormapsize[] = { 2, 4, 16, 256, 256 };        /* 2 for zero is an odd property of GIF */
           40 
           41 static void                writeheader(Biobuf*, Rectangle, int, ulong, int);
           42 static void                writedescriptor(Biobuf*, Rectangle);
           43 static char*        writedata(Biobuf*, Image*, Memimage*);
           44 static void                writecomment(Biobuf *fd, char*);
           45 static void                writegraphiccontrol(Biobuf *fd, int, int);
           46 static void*        gifmalloc(ulong);
           47 static void                encode(Biobuf*, Rectangle, int, uchar*, uint);
           48 
           49 static
           50 char*
           51 startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount)
           52 {
           53         int i;
           54 
           55         for(i=0; i<nelem(tbl); i++){
           56                 tbl[i].index = i;
           57                 tbl[i].prefix = -1;
           58                 tbl[i].exten = i;
           59         }
           60 
           61         switch(chan){
           62         case GREY1:
           63         case GREY2:
           64         case GREY4:
           65         case CMAP8:
           66         case GREY8:
           67                 break;
           68         default:
           69                 return "WriteGIF: can't handle channel type";
           70         }
           71 
           72         mainrect = r;
           73         writeheader(fd, r, depth, chan, loopcount);
           74         return nil;
           75 }
           76 
           77 char*
           78 startgif(Biobuf *fd, Image *image, int loopcount)
           79 {
           80         return startgif0(fd, image->chan, image->r, image->depth, loopcount);
           81 }
           82 
           83 char*
           84 memstartgif(Biobuf *fd, Memimage *memimage, int loopcount)
           85 {
           86         return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount);
           87 }
           88 
           89 static
           90 char*
           91 writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans)
           92 {
           93         char *err;
           94 
           95         switch(chan){
           96         case GREY1:
           97         case GREY2:
           98         case GREY4:
           99         case CMAP8:
          100         case GREY8:
          101                 break;
          102         default:
          103                 return "WriteGIF: can't handle channel type";
          104         }
          105 
          106         writecomment(fd, comment);
          107         writegraphiccontrol(fd, dt, trans);
          108         writedescriptor(fd, r);
          109 
          110         err = writedata(fd, image, memimage);
          111         if(err != nil)
          112                 return err;
          113 
          114         return nil;
          115 }
          116 
          117 char*
          118 writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans)
          119 {
          120         return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans);
          121 }
          122 
          123 char*
          124 memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans)
          125 {
          126         return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans);
          127 }
          128 
          129 /*
          130  * Write little-endian 16-bit integer
          131  */
          132 static
          133 void
          134 put2(Biobuf *fd, int i)
          135 {
          136         Bputc(fd, i);
          137         Bputc(fd, i>>8);
          138 }
          139 
          140 /*
          141  * Get color map for all ldepths, in format suitable for writing out
          142  */
          143 static
          144 void
          145 getcolormap(void)
          146 {
          147         int i, col;
          148         ulong rgb;
          149         uchar *c;
          150 
          151         if(colormap[0] != nil)
          152                 return;
          153         for(i=0; i<nelem(colormap); i++)
          154                 colormap[i] = gifmalloc(3* colormapsize[i]);
          155         c = colormap[GREYMAP];        /* GREY8 */
          156         for(i=0; i<256; i++){
          157                 c[3*i+0] = i;        /* red */
          158                 c[3*i+1] = i;        /* green */
          159                 c[3*i+2] = i;        /* blue */
          160         }
          161         c = colormap[3];        /* RGBV */
          162         for(i=0; i<256; i++){
          163                 rgb = cmap2rgb(i);
          164                 c[3*i+0] = (rgb>>16) & 0xFF;        /* red */
          165                 c[3*i+1] = (rgb>> 8) & 0xFF;        /* green */
          166                 c[3*i+2] = (rgb>> 0) & 0xFF;        /* blue */
          167         }
          168         c = colormap[2];        /* GREY4 */
          169         for(i=0; i<16; i++){
          170                 col = (i<<4)|i;
          171                 rgb = cmap2rgb(col);
          172                 c[3*i+0] = (rgb>>16) & 0xFF;        /* red */
          173                 c[3*i+1] = (rgb>> 8) & 0xFF;        /* green */
          174                 c[3*i+2] = (rgb>> 0) & 0xFF;        /* blue */
          175         }
          176         c = colormap[1];        /* GREY2 */
          177         for(i=0; i<4; i++){
          178                 col = (i<<6)|(i<<4)|(i<<2)|i;
          179                 rgb = cmap2rgb(col);
          180                 c[3*i+0] = (rgb>>16) & 0xFF;        /* red */
          181                 c[3*i+1] = (rgb>> 8) & 0xFF;        /* green */
          182                 c[3*i+2] = (rgb>> 0) & 0xFF;        /* blue */
          183         }
          184         c = colormap[0];        /* GREY1 */
          185         for(i=0; i<2; i++){
          186                 if(i == 0)
          187                         col = 0;
          188                 else
          189                         col = 0xFF;
          190                 rgb = cmap2rgb(col);
          191                 c[3*i+0] = (rgb>>16) & 0xFF;        /* red */
          192                 c[3*i+1] = (rgb>> 8) & 0xFF;        /* green */
          193                 c[3*i+2] = (rgb>> 0) & 0xFF;        /* blue */
          194         }
          195 }
          196 
          197 /*
          198  * Write header, logical screen descriptor, and color map
          199  */
          200 static
          201 void
          202 writeheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount)
          203 {
          204         /* Header */
          205         Bprint(fd, "%s", "GIF89a");
          206 
          207         /*  Logical Screen Descriptor */
          208         put2(fd, Dx(r));
          209         put2(fd, Dy(r));
          210 
          211         /* Color table present, 4 bits per color (for RGBV best case), size of color map */
          212         Bputc(fd, (1<<7)|(3<<4)|(depth-1));        /* not right for GREY8, but GIF doesn't let us specify enough bits */
          213         Bputc(fd, 0xFF);        /* white background (doesn't matter anyway) */
          214         Bputc(fd, 0);        /* pixel aspect ratio - unused */
          215 
          216         /* Global Color Table */
          217         getcolormap();
          218         if(chan == GREY8)
          219                 depth = GREYMAP;
          220         else
          221                 depth = drawlog2[depth];
          222         Bwrite(fd, colormap[depth], 3*colormapsize[depth]);
          223 
          224         if(loopcount >= 0){        /* hard-to-discover way to force cycled animation */
          225                 /* Application Extension with (1 loopcountlo loopcounthi) as data */
          226                 Bputc(fd, 0x21);
          227                 Bputc(fd, 0xFF);
          228                 Bputc(fd, 11);
          229                 Bwrite(fd, "NETSCAPE2.0", 11);
          230                 Bputc(fd, 3);
          231                 Bputc(fd, 1);
          232                 put2(fd, loopcount);
          233                 Bputc(fd, 0);
          234         }
          235 }
          236 
          237 /*
          238  * Write optional comment block
          239  */
          240 static
          241 void
          242 writecomment(Biobuf *fd, char *comment)
          243 {
          244         int n;
          245 
          246         if(comment==nil || comment[0]=='\0')
          247                 return;
          248 
          249         /* Comment extension and label */
          250         Bputc(fd, 0x21);
          251         Bputc(fd, 0xFE);
          252 
          253         /* Comment data */
          254         n = strlen(comment);
          255         if(n > 255)
          256                 n = 255;
          257         Bputc(fd, n);
          258         Bwrite(fd, comment, n);
          259 
          260         /* Block terminator */
          261         Bputc(fd, 0x00);
          262 }
          263 
          264 /*
          265  * Write optional control block (sets Delay Time)
          266  */
          267 static
          268 void
          269 writegraphiccontrol(Biobuf *fd, int dt, int trans)
          270 {
          271         if(dt < 0 && trans < 0)
          272                 return;
          273 
          274         /* Comment extension and label and block size*/
          275         Bputc(fd, 0x21);
          276         Bputc(fd, 0xF9);
          277         Bputc(fd, 0x04);
          278 
          279         /* Disposal method and other flags (none) */
          280         if(trans >= 0)
          281                 Bputc(fd, 0x01);
          282         else
          283                 Bputc(fd, 0x00);
          284 
          285         /* Delay time, in centisec (argument is millisec for sanity) */
          286         if(dt < 0)
          287                 dt = 0;
          288         else if(dt < 10)
          289                 dt = 1;
          290         else
          291                 dt = (dt+5)/10;
          292         put2(fd, dt);
          293 
          294         /* Transparency index */
          295         if(trans < 0)
          296                 trans = 0;
          297         Bputc(fd, trans);
          298 
          299         /* Block terminator */
          300         Bputc(fd, 0x00);
          301 }
          302 
          303 /*
          304  * Write image descriptor
          305  */
          306 static
          307 void
          308 writedescriptor(Biobuf *fd, Rectangle r)
          309 {
          310         /* Image Separator */
          311         Bputc(fd, 0x2C);
          312 
          313         /* Left, top, width, height */
          314         put2(fd, r.min.x-mainrect.min.x);
          315         put2(fd, r.min.y-mainrect.min.y);
          316         put2(fd, Dx(r));
          317         put2(fd, Dy(r));
          318         /* no special processing */
          319         Bputc(fd, 0);
          320 }
          321 
          322 /*
          323  * Write data
          324  */
          325 static
          326 char*
          327 writedata(Biobuf *fd, Image *image, Memimage *memimage)
          328 {
          329         char *err;
          330         uchar *data;
          331         int ndata, depth;
          332         Rectangle r;
          333 
          334         if(memimage != nil){
          335                 r = memimage->r;
          336                 depth = memimage->depth;
          337         }else{
          338                 r = image->r;
          339                 depth = image->depth;
          340         }
          341 
          342         /* LZW Minimum code size */
          343         if(depth == 1)
          344                 Bputc(fd, 2);
          345         else
          346                 Bputc(fd, depth);
          347 
          348         /*
          349          * Read image data into memory
          350          * potentially one extra byte on each end of each scan line
          351          */
          352         ndata = Dy(r)*(2+(Dx(r)>>(3-drawlog2[depth])));
          353         data = gifmalloc(ndata);
          354         if(memimage != nil)
          355                 ndata = unloadmemimage(memimage, r, data, ndata);
          356         else
          357                 ndata = unloadimage(image, r, data, ndata);
          358         if(ndata < 0){
          359                 err = gifmalloc(ERRMAX);
          360                 snprint(err, ERRMAX, "WriteGIF: %r");
          361                 free(data);
          362                 return err;
          363         }
          364 
          365         /* Encode and emit the data */
          366         encode(fd, r, depth, data, ndata);
          367         free(data);
          368 
          369         /*  Block Terminator */
          370         Bputc(fd, 0);
          371         return nil;
          372 }
          373 
          374 /*
          375  * Write trailer
          376  */
          377 void
          378 endgif(Biobuf *fd)
          379 {
          380         Bputc(fd, 0x3B);
          381         Bflush(fd);
          382 }
          383 
          384 void
          385 memendgif(Biobuf *fd)
          386 {
          387         endgif(fd);
          388 }
          389 
          390 /*
          391  * Put n bits of c into output at io.buf[i];
          392  */
          393 static
          394 void
          395 output(IO *io, int c, int n)
          396 {
          397         if(c < 0){
          398                 if(io->nbits != 0)
          399                         io->buf[io->i++] = io->sreg;
          400                 Bputc(io->fd, io->i);
          401                 Bwrite(io->fd, io->buf, io->i);
          402                 io->nbits = 0;
          403                 return;
          404         }
          405 
          406         if(io->nbits+n >= 31){
          407                 fprint(2, "panic: WriteGIF sr overflow\n");
          408                 exits("WriteGIF panic");
          409         }
          410         io->sreg |= c<<io->nbits;
          411         io->nbits += n;
          412 
          413         while(io->nbits >= 8){
          414                 io->buf[io->i++] = io->sreg;
          415                 io->sreg >>= 8;
          416                 io->nbits -= 8;
          417         }
          418 
          419         if(io->i >= 255){
          420                 Bputc(io->fd, 255);
          421                 Bwrite(io->fd, io->buf, 255);
          422                 memmove(io->buf, io->buf+255, io->i-255);
          423                 io->i -= 255;
          424         }
          425 }
          426 
          427 /*
          428  * LZW encoder
          429  */
          430 static
          431 void
          432 encode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata)
          433 {
          434         int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel;
          435         int CTM, EOD, codesize, ld0, datai, x, ld, pm;
          436         int nentry, maxentry, early;
          437         Entry *e, *oe;
          438         IO *io;
          439         Entry **hash;
          440 
          441         first = 1;
          442         ld = drawlog2[depth];
          443         /* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */
          444         ld0 = ld;
          445         if(ld0 == 0)
          446                 ld0 = 1;
          447         codesize = (1<<ld0);
          448         CTM = 1<<codesize;
          449         EOD = CTM+1;
          450 
          451         io = gifmalloc(sizeof(IO));
          452         io->fd = fd;
          453         sreg = 0;
          454         nbits = 0;
          455         bitsperpixel = 1<<ld;
          456         pm = (1<<bitsperpixel)-1;
          457 
          458         datai = 0;
          459         x = r.min.x;
          460         hash = gifmalloc(Nhash*sizeof(Entry*));
          461 
          462 Init:
          463         memset(hash, 0, Nhash*sizeof(Entry*));
          464         csize = codesize+1;
          465         nentry = EOD+1;
          466         maxentry = (1<<csize);
          467         for(i = 0; i<nentry; i++){
          468                 e = &tbl[i];
          469                 h = (e->prefix<<24) | (e->exten<<8);
          470                 h %= Nhash;
          471                 if(h < 0)
          472                         h += Nhash;
          473                 e->next = hash[h];
          474                 hash[h] = e;
          475         }
          476         prefix = -1;
          477         if(first)
          478                 output(io, CTM, csize);
          479         first = 0;
          480 
          481         /*
          482          * Scan over pixels.  Because of partially filled bytes on ends of scan lines,
          483          * which must be ignored in the data stream passed to GIF, this is more
          484          * complex than we'd like.
          485          */
          486 Next:
          487         for(;;){
          488                 if(ld != 3){
          489                         /* beginning of scan line is difficult; prime the shift register */
          490                         if(x == r.min.x){
          491                                 if(datai == ndata)
          492                                         break;
          493                                 sreg = data[datai++];
          494                                 nbits = 8-((x&(7>>ld))<<ld);
          495                         }
          496                         x++;
          497                         if(x == r.max.x)
          498                                 x = r.min.x;
          499                 }
          500                 if(nbits == 0){
          501                         if(datai == ndata)
          502                                 break;
          503                         sreg = data[datai++];
          504                         nbits = 8;
          505                 }
          506                 nbits -= bitsperpixel;
          507                 c = sreg>>nbits & pm;
          508                 h = prefix<<24 | c<<8;
          509                 h %= Nhash;
          510                 if(h < 0)
          511                         h += Nhash;
          512                 oe = nil;
          513                 for(e = hash[h]; e!=nil; e=e->next){
          514                         if(e->prefix == prefix && e->exten == c){
          515                                 if(oe != nil){
          516                                         oe->next = e->next;
          517                                         e->next = hash[h];
          518                                         hash[h] = e;
          519                                 }
          520                                 prefix = e->index;
          521                                 goto Next;
          522                         }
          523                         oe = e;
          524                 }
          525 
          526                 output(io, prefix, csize);
          527                 early = 0; /* peculiar tiff feature here for reference */
          528                 if(nentry == maxentry-early){
          529                         if(csize == 12){
          530                                 nbits += bitsperpixel;        /* unget pixel */
          531                                 x--;
          532                                 if(ld != 3 && x == r.min.x)
          533                                         datai--;
          534                                 output(io, CTM, csize);
          535                                 goto Init;
          536                         }
          537                         csize++;
          538                         maxentry = (1<<csize);
          539                 }
          540 
          541                 e = &tbl[nentry];
          542                 e->prefix = prefix;
          543                 e->exten = c;
          544                 e->next = hash[h];
          545                 hash[h] = e;
          546 
          547                 prefix = c;
          548                 nentry++;
          549         }
          550 
          551         output(io, prefix, csize);
          552         output(io, EOD, csize);
          553         output(io, -1, csize);
          554         free(io);
          555         free(hash);
          556 }
          557 
          558 static
          559 void*
          560 gifmalloc(ulong sz)
          561 {
          562         void *v;
          563         v = malloc(sz);
          564         if(v == nil) {
          565                 fprint(2, "WriteGIF: out of memory allocating %ld\n", sz);
          566 abort();
          567                 exits("mem");
          568         }
          569         memset(v, 0, sz);
          570         return v;
          571 }