00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 #ifdef HAVE_CONFIG_H
00015 # include "config.h"
00016 #endif
00017 #include "gif.h"
00018 #include <string.h>
00019 #include <stdarg.h>
00020 #ifdef __cplusplus
00021 extern "C" {
00022 #endif
00023 
00024 
00025 Gif_Stream *
00026 Gif_NewStream(void)
00027 {
00028   Gif_Stream *gfs = Gif_New(Gif_Stream);
00029   if (!gfs) return 0;
00030   gfs->global = 0;
00031   gfs->background = 0;
00032   gfs->screen_width = gfs->screen_height = 0;
00033   gfs->loopcount = -1;
00034   gfs->comment = 0;
00035   gfs->images = 0;
00036   gfs->nimages = gfs->imagescap = 0;
00037   gfs->extensions = 0;
00038   gfs->errors = 0;
00039   gfs->userflags = 0;
00040   gfs->refcount = 0;
00041   return gfs;
00042 }
00043 
00044 
00045 Gif_Image *
00046 Gif_NewImage(void)
00047 {
00048   Gif_Image *gfi = Gif_New(Gif_Image);
00049   if (!gfi) return 0;
00050   gfi->identifier = 0;
00051   gfi->comment = 0;
00052   gfi->local = 0;
00053   gfi->transparent = -1;
00054   gfi->disposal = GIF_DISPOSAL_NONE;
00055   gfi->delay = 0;
00056   gfi->left = gfi->top = gfi->width = gfi->height = 0;
00057   gfi->interlace = 0;
00058   gfi->img = 0;
00059   gfi->image_data = 0;
00060   gfi->free_image_data = Gif_DeleteArrayFunc;
00061   gfi->compressed_len = 0;
00062   gfi->compressed = 0;
00063   gfi->free_compressed = 0;
00064   gfi->user_data = 0;
00065   gfi->free_user_data = 0;
00066   gfi->refcount = 0;
00067   return gfi;
00068 }
00069 
00070 
00071 Gif_Colormap *
00072 Gif_NewColormap(void)
00073 {
00074   Gif_Colormap *gfcm = Gif_New(Gif_Colormap);
00075   if (!gfcm) return 0;
00076   gfcm->ncol = 0;
00077   gfcm->capacity = 0;
00078   gfcm->col = 0;
00079   gfcm->refcount = 0;
00080   gfcm->userflags = 0;
00081   return gfcm;
00082 }
00083 
00084 
00085 Gif_Colormap *
00086 Gif_NewFullColormap(int count, int capacity)
00087 {
00088   Gif_Colormap *gfcm = Gif_New(Gif_Colormap);
00089   if (!gfcm || capacity <= 0 || count < 0) return 0;
00090   if (count > capacity) capacity = count;
00091   gfcm->ncol = count;
00092   gfcm->capacity = capacity;
00093   gfcm->col = Gif_NewArray(Gif_Color, capacity);
00094   gfcm->refcount = 0;
00095   gfcm->userflags = 0;
00096   if (!gfcm->col) {
00097     Gif_Delete(gfcm);
00098     return 0;
00099   } else
00100     return gfcm;
00101 }
00102 
00103 
00104 Gif_Comment *
00105 Gif_NewComment(void)
00106 {
00107   Gif_Comment *gfcom = Gif_New(Gif_Comment);
00108   if (!gfcom) return 0;
00109   gfcom->str = 0;
00110   gfcom->len = 0;
00111   gfcom->count = gfcom->cap = 0;
00112   return gfcom;
00113 }
00114 
00115 
00116 Gif_Extension *
00117 Gif_NewExtension(int kind, char *app_name)
00118 {
00119   Gif_Extension *gfex = Gif_New(Gif_Extension);
00120   if (!gfex) return 0;
00121   gfex->kind = app_name ? 255 : kind;
00122   gfex->application = Gif_CopyString(app_name);
00123   gfex->data = 0;
00124   gfex->position = 0;
00125   gfex->stream = 0;
00126   gfex->next = 0;
00127   gfex->free_data = 0;
00128   if (!gfex->application && app_name) {
00129     Gif_DeleteExtension(gfex);
00130     return 0;
00131   }
00132   return gfex;
00133 }
00134 
00135 
00136 char *
00137 Gif_CopyString(char *s)
00138 {
00139   int l;
00140   char *copy;
00141   if (!s) return 0;
00142   l = strlen(s);
00143   copy = Gif_NewArray(char, l + 1);
00144   if (!copy) return 0;
00145   memcpy(copy, s, l + 1);
00146   return copy;
00147 }
00148 
00149 
00150 int
00151 Gif_AddImage(Gif_Stream *gfs, Gif_Image *gfi)
00152 {
00153   if (gfs->nimages >= gfs->imagescap) {
00154     if (gfs->imagescap) gfs->imagescap *= 2;
00155     else gfs->imagescap = 2;
00156     Gif_ReArray(gfs->images, Gif_Image *, gfs->imagescap);
00157     if (!gfs->images) return 0;
00158   }
00159   gfs->images[gfs->nimages] = gfi;
00160   gfs->nimages++;
00161   gfi->refcount++;
00162   return 1;
00163 }
00164 
00165 
00166 void
00167 Gif_RemoveImage(Gif_Stream *gfs, int inum)
00168 {
00169   int j;
00170   if (inum < 0 || inum >= gfs->nimages)
00171     return;
00172   Gif_DeleteImage(gfs->images[inum]);
00173   for (j = inum; j < gfs->nimages - 1; j++)
00174     gfs->images[j] = gfs->images[j+1];
00175   gfs->nimages--;
00176 }
00177 
00178 
00179 int
00180 Gif_AddCommentTake(Gif_Comment *gfcom, char *x, int xlen)
00181 {
00182   if (gfcom->count >= gfcom->cap) {
00183     if (gfcom->cap) gfcom->cap *= 2;
00184     else gfcom->cap = 2;
00185     Gif_ReArray(gfcom->str, char *, gfcom->cap);
00186     Gif_ReArray(gfcom->len, int, gfcom->cap);
00187     if (!gfcom->str || !gfcom->len) return 0;
00188   }
00189   if (xlen < 0) xlen = strlen(x);
00190   gfcom->str[ gfcom->count ] = x;
00191   gfcom->len[ gfcom->count ] = xlen;
00192   gfcom->count++;
00193   return 1;
00194 }
00195 
00196 
00197 int
00198 Gif_AddComment(Gif_Comment *gfcom, char *x, int xlen)
00199 {
00200   char *new_x;
00201   if (xlen < 0) xlen = strlen(x);
00202   new_x = Gif_NewArray(char, xlen);
00203   if (!new_x) return 0;
00204   memcpy(new_x, x, xlen);
00205   if (Gif_AddCommentTake(gfcom, new_x, xlen) == 0) {
00206     Gif_DeleteArray(new_x);
00207     return 0;
00208   } else
00209     return 1;
00210 }
00211 
00212 
00213 int
00214 Gif_AddExtension(Gif_Stream *gfs, Gif_Extension *gfex, int pos)
00215 {
00216   Gif_Extension *prev, *trav;
00217   if (gfex->stream) return 0;
00218   for (prev = 0, trav = gfs->extensions;
00219        trav && trav->position <= pos;
00220        prev = trav, trav = trav->next)
00221     ;
00222   if (prev) prev->next = gfex;
00223   else gfs->extensions = gfex;
00224   gfex->next = trav;
00225   return 1;
00226 }
00227 
00228 
00229 int
00230 Gif_ImageNumber(Gif_Stream *gfs, Gif_Image *gfi)
00231 {
00232   int i;
00233   for (i = 0; i < gfs->nimages; i++)
00234     if (gfs->images[i] == gfi)
00235       return i;
00236   return -1;
00237 }
00238 
00239 
00240 void
00241 Gif_CalculateScreenSize(Gif_Stream *gfs, int force)
00242 {
00243   int i;
00244   int screen_width = 0;
00245   int screen_height = 0;
00246   
00247   for (i = 0; i < gfs->nimages; i++) {
00248     Gif_Image *gfi = gfs->images[i];
00249     
00250     
00251     if (screen_width < gfi->left + gfi->width)
00252       screen_width = gfi->left + gfi->width;
00253     if (screen_height < gfi->top + gfi->height)
00254       screen_height = gfi->top + gfi->height;
00255   }
00256   
00257   
00258 
00259   if (screen_width == 0 && (force || gfs->screen_width == 0))
00260     screen_width = 640;
00261   if (screen_height == 0 && (force || gfs->screen_height == 0))
00262     screen_height = 480;
00263   
00264   if (gfs->screen_width < screen_width || force)
00265     gfs->screen_width = screen_width;
00266   if (gfs->screen_height < screen_height || force)
00267     gfs->screen_height = screen_height;
00268 }
00269 
00270 
00271 Gif_Stream *
00272 Gif_CopyStreamSkeleton(Gif_Stream *gfs)
00273 {
00274   Gif_Stream *ngfs = Gif_NewStream();
00275   ngfs->global = Gif_CopyColormap(gfs->global);
00276   ngfs->background = gfs->background;
00277   ngfs->screen_width = gfs->screen_width;
00278   ngfs->screen_height = gfs->screen_height;
00279   ngfs->loopcount = gfs->loopcount;
00280   if (gfs->global && !ngfs->global) {
00281     Gif_DeleteStream(ngfs);
00282     return 0;
00283   } else
00284     return ngfs;
00285 }
00286 
00287 
00288 Gif_Stream *
00289 Gif_CopyStreamImages(Gif_Stream *gfs)
00290 {
00291   Gif_Stream *ngfs = Gif_CopyStreamSkeleton(gfs);
00292   int i;
00293   if (!ngfs) return 0;
00294   for (i = 0; i < gfs->nimages; i++) {
00295     Gif_Image *gfi = Gif_CopyImage(gfs->images[i]);
00296     if (!gfi || !Gif_AddImage(ngfs, gfi)) {
00297       Gif_DeleteStream(ngfs);
00298       return 0;
00299     }
00300   }
00301   return ngfs;
00302 }
00303 
00304 
00305 Gif_Colormap *
00306 Gif_CopyColormap(Gif_Colormap *src)
00307 {
00308   int i;
00309   Gif_Colormap *dest;
00310   if (!src) return 0;
00311   
00312   dest = Gif_NewFullColormap(src->ncol, src->capacity);
00313   if (!dest) return 0;
00314   
00315   for (i = 0; i < src->ncol; i++) {
00316     dest->col[i] = src->col[i];
00317     dest->col[i].haspixel = 0;
00318   }
00319   
00320   return dest;
00321 }
00322 
00323 
00324 Gif_Image *
00325 Gif_CopyImage(Gif_Image *src)
00326 {
00327   Gif_Image *dest;
00328   byte *data;
00329   int i;
00330   if (!src) return 0;
00331   
00332   dest = Gif_NewImage();
00333   if (!dest) return 0;
00334   
00335   dest->identifier = Gif_CopyString(src->identifier);
00336   if (!dest->identifier && src->identifier) goto failure;
00337   if (src->comment) {
00338     dest->comment = Gif_NewComment();
00339     if (!dest->comment) goto failure;
00340     for (i = 0; i < src->comment->count; i++)
00341       if (!Gif_AddComment(dest->comment, src->comment->str[i],
00342                           src->comment->len[i]))
00343         goto failure;
00344   }
00345   
00346   dest->local = Gif_CopyColormap(src->local);
00347   if (!dest->local && src->local) goto failure;
00348   dest->transparent = src->transparent;
00349   
00350   dest->delay = src->delay;
00351   dest->disposal = src->disposal;
00352   dest->left = src->left;
00353   dest->top = src->top;
00354   
00355   dest->width = src->width;
00356   dest->height = src->height;
00357   
00358   dest->interlace = src->interlace;
00359   if (src->img) {
00360     dest->img = Gif_NewArray(byte *, dest->height + 1);
00361     dest->image_data = Gif_NewArray(byte, dest->width * dest->height);
00362     dest->free_image_data = Gif_DeleteArrayFunc;
00363     if (!dest->img || !dest->image_data) goto failure;
00364     for (i = 0, data = dest->image_data; i < dest->height; i++) {
00365       memcpy(data, src->img[i], dest->width);
00366       dest->img[i] = data;
00367       data += dest->width;
00368     }
00369     dest->img[dest->height] = 0;
00370   }
00371   if (src->compressed) {
00372     if (src->free_compressed == 0)
00373       dest->compressed = src->compressed;
00374     else {
00375       dest->compressed = Gif_NewArray(byte, src->compressed_len);
00376       dest->free_compressed = Gif_DeleteArrayFunc;
00377       memcpy(dest->compressed, src->compressed, src->compressed_len);
00378     }
00379     dest->compressed_len = src->compressed_len;
00380   }
00381   
00382   return dest;
00383   
00384  failure:
00385   Gif_DeleteImage(dest);
00386   return 0;
00387 }
00388 
00389 
00390 
00391 
00392 typedef struct Gif_DeletionHook {
00393   int kind;
00394   Gif_DeletionHookFunc func;
00395   void *callback_data;
00396   struct Gif_DeletionHook *next;
00397 } Gif_DeletionHook;
00398 
00399 static Gif_DeletionHook *all_hooks;
00400 
00401 void
00402 Gif_DeleteStream(Gif_Stream *gfs)
00403 {
00404   Gif_Extension *gfex;
00405   Gif_DeletionHook *hook;
00406   int i;
00407   if (!gfs) return;
00408   if (--gfs->refcount > 0) return;
00409   
00410   Gif_DeleteColormap(gfs->global);
00411   Gif_DeleteComment(gfs->comment);
00412   
00413   for (i = 0; i < gfs->nimages; i++)
00414     Gif_DeleteImage(gfs->images[i]);
00415   Gif_DeleteArray(gfs->images);
00416   
00417   gfex = gfs->extensions;
00418   while (gfex) {
00419     Gif_Extension *next = gfex->next;
00420     gfex->stream = 0;
00421     Gif_DeleteExtension(gfex);
00422     gfex = next;
00423   }
00424   
00425   for (hook = all_hooks; hook; hook = hook->next)
00426     if (hook->kind == GIF_T_STREAM)
00427       (*hook->func)(GIF_T_STREAM, gfs, hook->callback_data);
00428   Gif_Delete(gfs);
00429 }
00430 
00431 
00432 void
00433 Gif_DeleteImage(Gif_Image *gfi)
00434 {
00435   Gif_DeletionHook *hook;
00436   if (!gfi) return;
00437   if (--gfi->refcount > 0) return;
00438   
00439   for (hook = all_hooks; hook; hook = hook->next)
00440     if (hook->kind == GIF_T_IMAGE)
00441       (*hook->func)(GIF_T_IMAGE, gfi, hook->callback_data);
00442   
00443   Gif_DeleteArray(gfi->identifier);
00444   Gif_DeleteComment(gfi->comment);
00445   Gif_DeleteColormap(gfi->local);
00446   if (gfi->image_data && gfi->free_image_data)
00447     (*gfi->free_image_data)((void *)gfi->image_data);
00448   Gif_DeleteArray(gfi->img);
00449   if (gfi->compressed && gfi->free_compressed)
00450     (*gfi->free_compressed)((void *)gfi->compressed);
00451   if (gfi->user_data && gfi->free_user_data)
00452     (*gfi->free_user_data)(gfi->user_data);
00453   Gif_Delete(gfi);
00454 }
00455 
00456 
00457 void
00458 Gif_DeleteColormap(Gif_Colormap *gfcm)
00459 {
00460   Gif_DeletionHook *hook;
00461   if (!gfcm) return;
00462   if (--gfcm->refcount > 0) return;
00463 
00464   for (hook = all_hooks; hook; hook = hook->next)
00465     if (hook->kind == GIF_T_COLORMAP)
00466       (*hook->func)(GIF_T_COLORMAP, gfcm, hook->callback_data);
00467   
00468   Gif_DeleteArray(gfcm->col);
00469   Gif_Delete(gfcm);
00470 }
00471 
00472 
00473 void
00474 Gif_DeleteComment(Gif_Comment *gfcom)
00475 {
00476   int i;
00477   if (!gfcom) return;
00478   for (i = 0; i < gfcom->count; i++)
00479     Gif_DeleteArray(gfcom->str[i]);
00480   Gif_DeleteArray(gfcom->str);
00481   Gif_DeleteArray(gfcom->len);
00482   Gif_Delete(gfcom);
00483 }
00484 
00485 
00486 void
00487 Gif_DeleteExtension(Gif_Extension *gfex)
00488 {
00489   if (!gfex) return;
00490   if (gfex->data && gfex->free_data)
00491     (*gfex->free_data)(gfex->data);
00492   Gif_DeleteArray(gfex->application);
00493   if (gfex->stream) {
00494     Gif_Stream *gfs = gfex->stream;
00495     Gif_Extension *prev, *trav;
00496     for (prev = 0, trav = gfs->extensions;
00497          trav && trav != gfex;
00498          prev = trav, trav = trav->next)
00499       ;
00500     if (trav) {
00501       if (prev) prev->next = trav->next;
00502       else gfs->extensions = trav->next;
00503     }
00504   }
00505   Gif_Delete(gfex);
00506 }
00507 
00508 
00509 
00510 
00511 int
00512 Gif_AddDeletionHook(int kind, void (*func)(int, void *, void *), void *cb)
00513 {
00514   Gif_DeletionHook *hook = Gif_New(Gif_DeletionHook);
00515   if (!hook) return 0;
00516   Gif_RemoveDeletionHook(kind, func, cb);
00517   hook->kind = kind;
00518   hook->func = func;
00519   hook->callback_data = cb;
00520   hook->next = all_hooks;
00521   all_hooks = hook;
00522   return 1;
00523 }
00524 
00525 void
00526 Gif_RemoveDeletionHook(int kind, void (*func)(int, void *, void *), void *cb)
00527 {
00528   Gif_DeletionHook *hook = all_hooks, *prev = 0;
00529   while (hook) {
00530     if (hook->kind == kind && hook->func == func
00531         && hook->callback_data == cb) {
00532       if (prev) prev->next = hook->next;
00533       else all_hooks = hook->next;
00534       Gif_Delete(hook);
00535       return;
00536     }
00537     prev = hook;
00538     hook = hook->next;
00539   }
00540 }
00541 
00542 
00543 int
00544 Gif_ColorEq(Gif_Color *c1, Gif_Color *c2)
00545 {
00546   return GIF_COLOREQ(c1, c2);
00547 }
00548 
00549 
00550 int
00551 Gif_FindColor(Gif_Colormap *gfcm, Gif_Color *c)
00552 {
00553   int i;
00554   for (i = 0; i < gfcm->ncol; i++)
00555     if (GIF_COLOREQ(&gfcm->col[i], c))
00556       return i;
00557   return -1;
00558 }
00559 
00560 
00561 int
00562 Gif_AddColor(Gif_Colormap *gfcm, Gif_Color *c, int look_from)
00563 {
00564   int i;
00565   if (look_from >= 0)
00566     for (i = look_from; i < gfcm->ncol; i++)
00567       if (GIF_COLOREQ(&gfcm->col[i], c))
00568         return i;
00569   if (gfcm->ncol >= gfcm->capacity) {
00570     gfcm->capacity *= 2;
00571     Gif_ReArray(gfcm->col, Gif_Color, gfcm->capacity);
00572     if (gfcm->col == 0) return -1;
00573   }
00574   i = gfcm->ncol;
00575   gfcm->ncol++;
00576   gfcm->col[i] = *c;
00577   return i;
00578 }
00579 
00580 
00581 Gif_Image *
00582 Gif_GetImage(Gif_Stream *gfs, int imagenumber)
00583 {
00584   if (imagenumber >= 0 && imagenumber < gfs->nimages)
00585     return gfs->images[imagenumber];
00586   else
00587     return 0;
00588 }
00589 
00590 
00591 Gif_Image *
00592 Gif_GetNamedImage(Gif_Stream *gfs, const char *name)
00593 {
00594   int i;
00595   
00596   if (!name)
00597     return gfs->nimages ? gfs->images[0] : 0;
00598   
00599   for (i = 0; i < gfs->nimages; i++)
00600     if (gfs->images[i]->identifier &&
00601         strcmp(gfs->images[i]->identifier, name) == 0)
00602       return gfs->images[i];
00603   
00604   return 0;
00605 }
00606 
00607 
00608 Gif_Extension *
00609 Gif_GetExtension(Gif_Stream *gfs, int id, Gif_Extension *search_from)
00610 {
00611   if (!search_from) search_from = gfs->extensions;
00612   while (search_from) {
00613     if (search_from->kind == id)
00614       return search_from;
00615     search_from = search_from->next;
00616   }
00617   return 0;
00618 }
00619 
00620 
00621 void
00622 Gif_ReleaseCompressedImage(Gif_Image *gfi)
00623 {
00624   if (gfi->compressed && gfi->free_compressed)
00625     (*gfi->free_compressed)(gfi->compressed);
00626   gfi->compressed = 0;
00627   gfi->compressed_len = 0;
00628   gfi->free_compressed = 0;
00629 }
00630 
00631 void
00632 Gif_ReleaseUncompressedImage(Gif_Image *gfi)
00633 {
00634   Gif_DeleteArray(gfi->img);
00635   if (gfi->image_data && gfi->free_image_data)
00636     (*gfi->free_image_data)(gfi->image_data);
00637   gfi->img = 0;
00638   gfi->image_data = 0;
00639   gfi->free_image_data = 0;
00640 }
00641 
00642 
00643 int
00644 Gif_ClipImage(Gif_Image *gfi, int left, int top, int width, int height)
00645 {
00646   int new_width = gfi->width, new_height = gfi->height;
00647   int y;
00648 
00649   if (!gfi->img) return 0;
00650   
00651   if (gfi->left < left) {
00652     int shift = left - gfi->left;
00653     for (y = 0; y < gfi->height; y++)
00654       gfi->img[y] += shift;
00655     gfi->left += shift;
00656     new_width -= shift;
00657   }
00658   
00659   if (gfi->top < top) {
00660     int shift = top - gfi->top;
00661     for (y = gfi->height - 1; y >= shift; y++)
00662       gfi->img[y - shift] = gfi->img[y];
00663     gfi->top += shift;
00664     new_height -= shift;
00665   }
00666   
00667   if (gfi->left + new_width >= width)
00668     new_width = width - gfi->left;
00669   
00670   if (gfi->top + new_height >= height)
00671     new_height = height - gfi->top;
00672   
00673   if (new_width < 0) new_width = 0;
00674   if (new_height < 0) new_height = 0;
00675   gfi->width = new_width;
00676   gfi->height = new_height;
00677   return 1;
00678 }
00679 
00680 
00681 int
00682 Gif_InterlaceLine(int line, int height)
00683 {
00684   height--;
00685   if (line > height / 2)
00686     return line * 2 - ( height       | 1);
00687   else if (line > height / 4)
00688     return line * 4 - ((height & ~1) | 2);
00689   else if (line > height / 8)
00690     return line * 8 - ((height & ~3) | 4);
00691   else
00692     return line * 8;
00693 }
00694 
00695 
00696 int
00697 Gif_SetUncompressedImage(Gif_Image *gfi, byte *image_data,
00698                          void (*free_data)(void *), int data_interlaced)
00699 {
00700   int i;
00701   int width = gfi->width;
00702   int height = gfi->height;
00703   byte **img;
00704   
00705   Gif_ReleaseUncompressedImage(gfi);
00706   if (!image_data) return 0;
00707   
00708   img = Gif_NewArray(byte *, height + 1);
00709   if (!img) return 0;
00710   
00711   if (data_interlaced)
00712     for (i = 0; i < height; i++)
00713       img[ Gif_InterlaceLine(i, height) ] = image_data + width * i;
00714   else
00715     for (i = 0; i < height; i++)
00716       img[i] = image_data + width * i;
00717   img[height] = 0;
00718   
00719   gfi->img = img;
00720   gfi->image_data = image_data;
00721   gfi->free_image_data = free_data;
00722   return 1;
00723 }
00724 
00725 int
00726 Gif_CreateUncompressedImage(Gif_Image *gfi)
00727 {
00728   byte *data = Gif_NewArray(byte, gfi->width * gfi->height);
00729   return Gif_SetUncompressedImage(gfi, data, Gif_DeleteArrayFunc,
00730                                   gfi->interlace);
00731 }
00732 
00733 
00734 void
00735 Gif_Debug(char *x, ...)
00736 {
00737   va_list val;
00738   va_start(val, x);
00739   vfprintf(stderr, x, val);
00740   fputc(' ', stderr);
00741   va_end(val);
00742 }
00743 
00744 #ifdef __cplusplus
00745 }
00746 #endif