00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 #ifdef HAVE_CONFIG_H
00013 # include "config.h"
00014 #elif !defined(__cplusplus) && !defined(inline)
00015 
00016 # define inline
00017 #endif
00018 #include "gif.h"
00019 #include <stdarg.h>
00020 #include <string.h>
00021 #ifdef __cplusplus
00022 extern "C" {
00023 #endif
00024 
00025 #define WRITE_BUFFER_SIZE       255
00026 
00027 typedef struct Gif_Context {
00028   
00029   Gif_Code *rle_next;
00030   
00031 } Gif_Context;
00032 
00033 
00034 typedef struct Gif_Writer {
00035   
00036   FILE *f;
00037   byte *v;
00038   u_int32_t pos;
00039   u_int32_t cap;
00040   int flags;
00041   void (*byte_putter)(byte, struct Gif_Writer *);
00042   void (*block_putter)(byte *, u_int16_t, struct Gif_Writer *);
00043   
00044 } Gif_Writer;
00045 
00046 
00047 #define gifputbyte(b, grr)      ((*grr->byte_putter)(b, grr))
00048 #define gifputblock(b, l, grr)  ((*grr->block_putter)(b, l, grr))
00049 
00050 static inline void
00051 gifputunsigned(u_int16_t uns, Gif_Writer *grr)
00052 {
00053   gifputbyte(uns & 0xFF, grr);
00054   gifputbyte(uns >> 8, grr);
00055 }
00056 
00057 
00058 static void
00059 file_byte_putter(byte b, Gif_Writer *grr)
00060 {
00061   fputc(b, grr->f);
00062 }
00063 
00064 static void
00065 file_block_putter(byte *block, u_int16_t size, Gif_Writer *grr)
00066 {
00067   fwrite(block, size, 1, grr->f);
00068 }
00069 
00070 
00071 static void
00072 memory_byte_putter(byte b, Gif_Writer *grr)
00073 {
00074   if (grr->pos >= grr->cap) {
00075     grr->cap *= 2;
00076     Gif_ReArray(grr->v, byte, grr->cap);
00077   }
00078   if (grr->v) {
00079     grr->v[grr->pos] = b;
00080     grr->pos++;
00081   }
00082 }
00083 
00084 static void
00085 memory_block_putter(byte *data, u_int16_t len, Gif_Writer *grr)
00086 {
00087   if (grr->pos + len >= grr->cap) {
00088     grr->cap *= 2;
00089     Gif_ReArray(grr->v, byte, grr->cap);
00090   }
00091   if (grr->v) {
00092     memcpy(grr->v + grr->pos, data, len);
00093     grr->pos += len;
00094   }
00095 }
00096 
00097 
00098 #ifdef GIF_NO_COMPRESSION
00099 
00100 static void
00101 real_write_image_data(byte **img, u_int16_t width, u_int16_t height,
00102                       byte min_code_bits, Gif_Context *gfc, Gif_Writer *grr)
00103 {
00104   byte buffer[WRITE_BUFFER_SIZE];
00105   byte *buf;
00106   
00107   u_int16_t xleft;
00108   byte *imageline;
00109   
00110   u_int32_t leftover;
00111   byte bits_left_over;
00112   
00113   Gif_Code next_code;
00114   Gif_Code output_code;
00115   Gif_Code clear_code;
00116   Gif_Code eoi_code;
00117   Gif_Code bump_code;
00118   
00119   byte cur_code_bits;
00120   
00121   
00122   gifputbyte(min_code_bits, grr);
00123   clear_code = 1 << min_code_bits;
00124   eoi_code = clear_code + 1;
00125   
00126   cur_code_bits = min_code_bits + 1;
00127   
00128   GIF_DEBUG(("clear(%d) eoi(%d)", clear_code, eoi_code));
00129   
00130   output_code = clear_code;
00131   
00132 
00133   
00134   bits_left_over = 0;
00135   leftover = 0;
00136   buf = buffer;
00137   xleft = width;
00138   imageline = img[0];
00139   
00140   
00141   while (1) {
00142     
00143     
00144 
00145     
00146     leftover |= output_code << bits_left_over;
00147     bits_left_over += cur_code_bits;
00148     while (bits_left_over >= 8) {
00149       *buf++ = leftover & 0xFF;
00150       leftover = (leftover >> 8) & 0x00FFFFFF;
00151       bits_left_over -= 8;
00152       if (buf == buffer + WRITE_BUFFER_SIZE) {
00153         GIF_DEBUG(("chunk"));
00154         gifputbyte(WRITE_BUFFER_SIZE, grr);
00155         gifputblock(buffer, WRITE_BUFFER_SIZE, grr);
00156         buf = buffer;
00157       }
00158     }
00159     
00160     if (output_code == clear_code) {
00161       
00162       cur_code_bits = min_code_bits + 1;
00163       next_code = eoi_code + 1;
00164       bump_code = clear_code << 1;
00165       
00166     } else if (next_code == bump_code) {
00167       
00168       
00169 
00170       output_code = clear_code;
00171       continue;
00172       
00173     } else if (output_code == eoi_code)
00174       break;
00175     
00176     
00177     
00178 
00179     
00180     
00181     if (height == 0)
00182       output_code = eoi_code;
00183     else {
00184       output_code = *imageline;
00185       if (output_code >= clear_code) output_code = 0;
00186       
00187       imageline++;
00188       xleft--;
00189       if (xleft == 0) {
00190         xleft = width;
00191         height--;
00192         img++;
00193         imageline = img[0];
00194       }
00195       
00196       next_code++;
00197     }
00198   }
00199   
00200   if (bits_left_over > 0)
00201     *buf++ = leftover;
00202   
00203   if (buf != buffer) {
00204     GIF_DEBUG(("imageblock(%d)", buf - buffer));
00205     gifputbyte(buf - buffer, grr);
00206     gifputblock(buffer, buf - buffer, grr);
00207   }
00208   
00209   gifputbyte(0, grr);
00210 }
00211 
00212 #else 
00213 
00214 
00215 
00216 
00217 
00218 static void
00219 real_write_image_data(byte **img, u_int16_t width, u_int16_t height,
00220                       byte min_code_bits, Gif_Context *gfc, Gif_Writer *grr)
00221 {
00222   byte buffer[WRITE_BUFFER_SIZE];
00223   byte *buf;
00224   
00225   u_int16_t xleft;
00226   byte *imageline;
00227   
00228   u_int32_t leftover;
00229   byte bits_left_over;
00230   
00231   Gif_Code next_code;
00232   Gif_Code output_code;
00233   Gif_Code clear_code;
00234   Gif_Code eoi_code;
00235   Gif_Code bump_code;
00236   byte suffix;
00237   
00238   Gif_Code *rle_next = gfc->rle_next;
00239   
00240   byte cur_code_bits;
00241   
00242   
00243   gifputbyte(min_code_bits, grr);
00244   clear_code = 1 << min_code_bits;
00245   eoi_code = clear_code + 1;
00246   
00247   cur_code_bits = min_code_bits + 1;
00248   
00249   GIF_DEBUG(("clear(%d) eoi(%d) bits(%d)",clear_code,eoi_code,cur_code_bits));
00250   
00251   output_code = clear_code;
00252   
00253 
00254   
00255   bits_left_over = 0;
00256   leftover = 0;
00257   buf = buffer;
00258   xleft = width;
00259   imageline = img[0];
00260   
00261   while (1) {
00262     
00263     
00264 
00265 
00266     leftover |= output_code << bits_left_over;
00267     bits_left_over += cur_code_bits;
00268     while (bits_left_over >= 8) {
00269       *buf++ = leftover & 0xFF;
00270       leftover = (leftover >> 8) & 0x00FFFFFF;
00271       bits_left_over -= 8;
00272       if (buf == buffer + WRITE_BUFFER_SIZE) {
00273         gifputbyte(WRITE_BUFFER_SIZE, grr);
00274         gifputblock(buffer, WRITE_BUFFER_SIZE, grr);
00275         buf = buffer;
00276       }
00277     }
00278     
00279     if (output_code == clear_code) {
00280       
00281       Gif_Code c;
00282       
00283       cur_code_bits = min_code_bits + 1;
00284       next_code = eoi_code + 1;
00285       bump_code = clear_code << 1;
00286       
00287       for (c = 0; c < clear_code; c++)
00288         rle_next[c] = clear_code;
00289       
00290     } else if (next_code > bump_code) {
00291       
00292       
00293       if (cur_code_bits == GIF_MAX_CODE_BITS) {
00294         output_code = clear_code;
00295         continue;
00296       } else {
00297         cur_code_bits++;
00298         bump_code <<= 1;
00299       }
00300       
00301     } else if (output_code == eoi_code)
00302       break;
00303     
00304     
00305     
00306 
00307     
00308     
00309     if (height == 0)
00310       output_code = eoi_code;
00311     else {
00312       output_code = suffix = *imageline;
00313       if (output_code >= clear_code) output_code = 0;
00314       goto next_pixel;
00315       
00316       while (height != 0 && *imageline == suffix
00317              && rle_next[output_code] != clear_code) {
00318         output_code = rle_next[output_code];
00319        next_pixel:
00320         imageline++;
00321         xleft--;
00322         if (xleft == 0) {
00323           xleft = width;
00324           height--;
00325           img++;
00326           imageline = img[0];
00327         }
00328       }
00329       
00330       if (height != 0 && *imageline == suffix) {
00331         rle_next[output_code] = next_code;
00332         rle_next[next_code] = clear_code;
00333       }
00334       next_code++;
00335     }
00336   }
00337   
00338   if (bits_left_over > 0)
00339     *buf++ = leftover;
00340   
00341   if (buf != buffer) {
00342     GIF_DEBUG(("imageblock(%d)", buf - buffer));
00343     gifputbyte(buf - buffer, grr);
00344     gifputblock(buffer, buf - buffer, grr);
00345   }
00346   
00347   gifputbyte(0, grr);
00348 }
00349 
00350 #endif 
00351 
00352 
00353 static int
00354 calculate_min_code_bits(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
00355 {
00356   int colors_used = -1, min_code_bits, i;
00357 
00358   if (grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
00359     
00360     if (gfi->local && gfi->local->ncol > 0)
00361       colors_used = gfi->local->ncol;
00362     else if (gfs->global && gfs->global->ncol > 0)
00363       colors_used = gfs->global->ncol;
00364     
00365   } else if (gfi->compressed) {
00366     
00367     colors_used = 1 << gfi->compressed[0];
00368   
00369   } else if (gfi->img) {
00370     
00371     int x, y, width = gfi->width, height = gfi->height;
00372     colors_used = 0;
00373     for (y = 0; y < height && colors_used < 128; y++) {
00374       byte *data = gfi->img[y];
00375       for (x = width; x > 0; x--, data++)
00376         if (*data > colors_used)
00377           colors_used = *data;
00378     }
00379     colors_used++;
00380     
00381   } else {
00382     
00383     colors_used = 256;
00384   }
00385   
00386   min_code_bits = 2;            
00387   i = 4;
00388   while (i < colors_used) {
00389     min_code_bits++;
00390     i *= 2;
00391   }
00392 
00393   if ((grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE)
00394       && gfi->compressed && gfi->compressed[0] != min_code_bits) {
00395     
00396     if (Gif_UncompressImage(gfi))
00397       Gif_FullCompressImage(gfs, gfi, grr->flags);
00398   }
00399   
00400   return min_code_bits;
00401 }
00402 
00403 static int
00404 write_image_data(Gif_Image *gfi, byte min_code_bits,
00405                  Gif_Context *gfc, Gif_Writer *grr)
00406 {
00407   byte **img = gfi->img;
00408   u_int16_t width = gfi->width, height = gfi->height;
00409     
00410   if (gfi->interlace) {
00411     u_int16_t y;
00412     byte **nimg = Gif_NewArray(byte *, height + 1);
00413     if (!nimg) return 0;
00414     
00415     for (y = 0; y < height; y++)
00416       nimg[y] = img[Gif_InterlaceLine(y, height)];
00417     nimg[height] = 0;
00418     
00419     real_write_image_data(nimg, width, height, min_code_bits, gfc, grr);
00420     
00421     Gif_DeleteArray(nimg);
00422   } else
00423     real_write_image_data(img, width, height, min_code_bits, gfc, grr);
00424   
00425   return 1;
00426 }
00427 
00428 
00429 int
00430 Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, int flags)
00431 {
00432   int ok = 0;
00433   byte min_code_bits;
00434   Gif_Writer grr;
00435   Gif_Context gfc;
00436   
00437   if (gfi->compressed && gfi->free_compressed) {
00438     (*gfi->free_compressed)((void *)gfi->compressed);
00439     gfi->compressed = 0;
00440   }
00441   
00442   gfc.rle_next = Gif_NewArray(Gif_Code, GIF_MAX_CODE);
00443   
00444   grr.v = Gif_NewArray(byte, 1024);
00445   grr.pos = 0;
00446   grr.cap = 1024;
00447   grr.byte_putter = memory_byte_putter;
00448   grr.block_putter = memory_block_putter;
00449   grr.flags = flags;
00450   
00451   if (!grr.v || !gfc.rle_next)
00452     goto done;
00453 
00454   min_code_bits = calculate_min_code_bits(gfs, gfi, &grr);
00455   ok = write_image_data(gfi, min_code_bits, &gfc, &grr);
00456   
00457  done:
00458   if (!ok) {
00459     Gif_DeleteArray(grr.v);
00460     grr.v = 0;
00461   }
00462   gfi->compressed = grr.v;
00463   gfi->compressed_len = grr.pos;
00464   gfi->free_compressed = Gif_DeleteArrayFunc;
00465   Gif_DeleteArray(gfc.rle_next);
00466   return grr.v != 0;
00467 }
00468 
00469 
00470 static void
00471 write_color_table(Gif_Color *c, int ncol, Gif_Writer *grr)
00472 {
00473   int totalcol, i;
00474   
00475   if (ncol <= 0) ncol = 0;
00476   if (ncol > 256) ncol = 256;
00477   
00478   
00479   
00480   for (totalcol = 2; totalcol < ncol; totalcol *= 2) ;
00481   
00482   for (i = 0; i < ncol; i++, c++) {
00483     gifputbyte(c->red, grr);
00484     gifputbyte(c->green, grr);
00485     gifputbyte(c->blue, grr);
00486   }
00487   
00488   
00489   for (; i < totalcol; i++) {
00490     gifputbyte(0, grr);
00491     gifputbyte(0, grr);
00492     gifputbyte(0, grr);
00493   }
00494 }
00495 
00496 
00497 static int
00498 write_image(Gif_Stream *gfs, Gif_Image *gfi, Gif_Context *gfc, Gif_Writer *grr)
00499 {
00500   byte min_code_bits, packed = 0;
00501   
00502   gifputbyte(',', grr);
00503   gifputunsigned(gfi->left, grr);
00504   gifputunsigned(gfi->top, grr);
00505   gifputunsigned(gfi->width, grr);
00506   gifputunsigned(gfi->height, grr);
00507   
00508   if (gfi->local && gfi->local->ncol > 0) {
00509     int size = 2;
00510     packed |= 0x80;
00511     while (size < gfi->local->ncol && size < 256)
00512       size *= 2, packed++;
00513   }
00514   
00515   if (gfi->interlace) packed |= 0x40;
00516   gifputbyte(packed, grr);
00517   
00518   if (gfi->local && gfi->local->ncol > 0)
00519     write_color_table(gfi->local->col, gfi->local->ncol, grr);
00520 
00521   
00522 
00523   min_code_bits = calculate_min_code_bits(gfs, gfi, grr);
00524   
00525   
00526 
00527 
00528   if (gfi->compressed) {
00529     byte *compressed = gfi->compressed;
00530     u_int32_t compressed_len = gfi->compressed_len;
00531     while (compressed_len > 0) {
00532       u_int16_t amt = (compressed_len > 0x7000 ? 0x7000 : compressed_len);
00533       gifputblock(compressed, amt, grr);
00534       compressed += amt;
00535       compressed_len -= amt;
00536     }
00537     
00538   } else
00539     write_image_data(gfi, min_code_bits, gfc, grr);
00540   
00541   return 1;
00542 }
00543 
00544 
00545 static void
00546 write_logical_screen_descriptor(Gif_Stream *gfs, Gif_Writer *grr)
00547 {
00548   byte packed = 0x70;           
00549 
00550   Gif_CalculateScreenSize(gfs, 0);
00551   gifputunsigned(gfs->screen_width, grr);
00552   gifputunsigned(gfs->screen_height, grr);
00553   
00554   if (gfs->global) {
00555     u_int16_t size = 2;
00556     packed |= 0x80;
00557     while (size < gfs->global->ncol && size < 256)
00558       size *= 2, packed++;
00559   }
00560   
00561   gifputbyte(packed, grr);
00562   gifputbyte(gfs->background, grr);
00563   gifputbyte(0, grr);           
00564   
00565   if (gfs->global)
00566     write_color_table(gfs->global->col, gfs->global->ncol, grr);
00567 }
00568 
00569 
00570 
00571 
00572 
00573 
00574 
00575 
00576 
00577 
00578 static void
00579 write_graphic_control_extension(Gif_Image *gfi, Gif_Writer *grr)
00580 {
00581   byte packed = 0;
00582   gifputbyte('!', grr);
00583   gifputbyte(0xF9, grr);
00584   gifputbyte(4, grr);
00585   if (gfi->transparent >= 0) packed |= 0x01;
00586   packed |= (gfi->disposal & 0x07) << 2;
00587   gifputbyte(packed, grr);
00588   gifputunsigned(gfi->delay, grr);
00589   gifputbyte((byte)gfi->transparent, grr);
00590   gifputbyte(0, grr);
00591 }
00592 
00593 
00594 static void
00595 blast_data(byte *data, int len, Gif_Writer *grr)
00596 {
00597   while (len > 0) {
00598     int s = len > 255 ? 255 : len;
00599     gifputbyte(s, grr);
00600     gifputblock(data, s, grr);
00601     data += s;
00602     len -= s;
00603   }
00604   gifputbyte(0, grr);
00605 }
00606 
00607 
00608 static void
00609 write_name_extension(char *id, Gif_Writer *grr)
00610 {
00611   gifputbyte('!', grr);
00612   gifputbyte(0xCE, grr);
00613   blast_data((byte *)id, strlen(id), grr);
00614 }
00615 
00616 
00617 static void
00618 write_comment_extensions(Gif_Comment *gfcom, Gif_Writer *grr)
00619 {
00620   int i;
00621   for (i = 0; i < gfcom->count; i++) {
00622     gifputbyte('!', grr);
00623     gifputbyte(0xFE, grr);
00624     blast_data((byte *)gfcom->str[i], gfcom->len[i], grr);
00625   }
00626 }
00627 
00628 
00629 static void
00630 write_netscape_loop_extension(u_int16_t value, Gif_Writer *grr)
00631 {
00632   gifputblock((byte *)"!\xFF\x0BNETSCAPE2.0\x03\x01", 16, grr);
00633   gifputunsigned(value, grr);
00634   gifputbyte(0, grr);
00635 }
00636 
00637 
00638 static void
00639 write_generic_extension(Gif_Extension *gfex, Gif_Writer *grr)
00640 {
00641   u_int32_t pos = 0;
00642   if (gfex->kind < 0) return;   
00643   
00644   gifputbyte('!', grr);
00645   gifputbyte(gfex->kind, grr);
00646   if (gfex->kind == 255) {      
00647     int len = gfex->application ? strlen(gfex->application) : 0;
00648     if (len) {
00649       gifputbyte(len, grr);
00650       gifputblock((byte *)gfex->application, len, grr);
00651     }
00652   }
00653   while (pos + 255 < gfex->length) {
00654     gifputbyte(255, grr);
00655     gifputblock(gfex->data + pos, 255, grr);
00656     pos += 255;
00657   }
00658   if (pos < gfex->length) {
00659     u_int32_t len = gfex->length - pos;
00660     gifputbyte(len, grr); 
00661     gifputblock(gfex->data + pos, len, grr);
00662   }
00663   gifputbyte(0, grr);
00664 }
00665 
00666 
00667 static int
00668 write_gif(Gif_Stream *gfs, Gif_Writer *grr)
00669 {
00670   int ok = 0;
00671   int i;
00672   Gif_Image *gfi;
00673   Gif_Extension *gfex = gfs->extensions;
00674   Gif_Context gfc;
00675   
00676   gfc.rle_next = Gif_NewArray(Gif_Code, GIF_MAX_CODE);
00677   if (!gfc.rle_next)
00678     goto done;
00679   
00680   {
00681     byte isgif89a = 0;
00682     if (gfs->comment || gfs->loopcount > -1)
00683       isgif89a = 1;
00684     for (i = 0; i < gfs->nimages && !isgif89a; i++) {
00685       gfi = gfs->images[i];
00686       if (gfi->identifier || gfi->transparent != -1 || gfi->disposal ||
00687           gfi->delay || gfi->comment)
00688         isgif89a = 1;
00689     }
00690     if (isgif89a)
00691       gifputblock((byte *)"GIF89a", 6, grr);
00692     else
00693       gifputblock((byte *)"GIF87a", 6, grr);
00694   }
00695   
00696   write_logical_screen_descriptor(gfs, grr);
00697   
00698   if (gfs->loopcount > -1)
00699     write_netscape_loop_extension(gfs->loopcount, grr);
00700   
00701   for (i = 0; i < gfs->nimages; i++) {
00702     Gif_Image *gfi = gfs->images[i];
00703     while (gfex && gfex->position == i) {
00704       write_generic_extension(gfex, grr);
00705       gfex = gfex->next;
00706     }
00707     if (gfi->comment)
00708       write_comment_extensions(gfi->comment, grr);
00709     if (gfi->identifier)
00710       write_name_extension(gfi->identifier, grr);
00711     if (gfi->transparent != -1 || gfi->disposal || gfi->delay)
00712       write_graphic_control_extension(gfi, grr);
00713     if (!write_image(gfs, gfi, &gfc, grr))
00714       goto done;
00715   }
00716   
00717   while (gfex) {
00718     write_generic_extension(gfex, grr);
00719     gfex = gfex->next;
00720   }
00721   if (gfs->comment)
00722     write_comment_extensions(gfs->comment, grr);
00723   
00724   gifputbyte(';', grr);
00725   ok = 1;
00726   
00727  done:
00728   Gif_DeleteArray(gfc.rle_next);
00729   return ok;
00730 }
00731 
00732 
00733 int
00734 Gif_FullWriteFile(Gif_Stream *gfs, int flags, FILE *f)
00735 {
00736   Gif_Writer grr;
00737   grr.f = f;
00738   grr.byte_putter = file_byte_putter;
00739   grr.block_putter = file_block_putter;
00740   grr.flags = flags;
00741   return write_gif(gfs, &grr);
00742 }
00743 
00744 
00745 #undef Gif_CompressImage
00746 #undef Gif_WriteFile
00747 
00748 int
00749 Gif_CompressImage(Gif_Stream *gfs, Gif_Image *gfi)
00750 {
00751   return Gif_FullCompressImage(gfs, gfi, 0);
00752 }
00753 
00754 int
00755 Gif_WriteFile(Gif_Stream *gfs, FILE *f)
00756 {
00757   return Gif_FullWriteFile(gfs, 0, f);
00758 }
00759 
00760 
00761 #ifdef __cplusplus
00762 }
00763 #endif