Doxygen Source Code Documentation
        
Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search   
xform.c
Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 #include "config.h"
00011 #include "gifsicle.h"
00012 #include <stdio.h>
00013 #include <string.h>
00014 #include <ctype.h>
00015 #include <assert.h>
00016 #include <errno.h>
00017 #include <limits.h>
00018 
00019 
00020 
00021 
00022 
00023 
00024 Gt_ColorTransform *
00025 append_color_transform(Gt_ColorTransform *list,
00026                        color_transform_func func, void *data)
00027 {
00028   Gt_ColorTransform *trav;
00029   Gt_ColorTransform *xform = Gif_New(Gt_ColorTransform);
00030   xform->next = 0;
00031   xform->func = func;
00032   xform->data = data;
00033   
00034   for (trav = list; trav && trav->next; trav = trav->next)
00035     ;
00036   if (trav) {
00037     trav->next = xform;
00038     return list;
00039   } else
00040     return xform;
00041 }
00042 
00043 Gt_ColorTransform *
00044 delete_color_transforms(Gt_ColorTransform *list, color_transform_func func)
00045 {
00046   Gt_ColorTransform *prev = 0, *trav = list;
00047   while (trav) {
00048     Gt_ColorTransform *next = trav->next;
00049     if (trav->func == func) {
00050       if (prev) prev->next = next;
00051       else list = next;
00052       Gif_Delete(trav);
00053     } else
00054       prev = trav;
00055     trav = next;
00056   }
00057   return list;
00058 }
00059 
00060 void
00061 apply_color_transforms(Gt_ColorTransform *list, Gif_Stream *gfs)
00062 {
00063   int i;
00064   Gt_ColorTransform *xform;
00065   for (xform = list; xform; xform = xform->next) {
00066     if (gfs->global)
00067       xform->func(gfs->global, xform->data);
00068     for (i = 0; i < gfs->nimages; i++)
00069       if (gfs->images[i]->local)
00070         xform->func(gfs->images[i]->local, xform->data);
00071   }
00072 }
00073 
00074 
00075 typedef struct Gt_ColorChange {
00076   struct Gt_ColorChange *next;
00077   Gif_Color old_color;
00078   Gif_Color new_color;
00079 } Gt_ColorChange;
00080 
00081 void
00082 color_change_transformer(Gif_Colormap *gfcm, void *thunk)
00083 {
00084   int i, have;
00085   Gt_ColorChange *first_change = (Gt_ColorChange *)thunk;
00086   Gt_ColorChange *change;
00087   
00088   
00089   for (i = 0; i < gfcm->ncol; i++)
00090     for (change = first_change; change; change = change->next) {
00091       if (!change->old_color.haspixel)
00092         have = GIF_COLOREQ(&gfcm->col[i], &change->old_color);
00093       else
00094         have = change->old_color.pixel == i;
00095       
00096       if (have) {
00097         gfcm->col[i] = change->new_color;
00098         break;                  
00099       }
00100     }
00101 }
00102 
00103 Gt_ColorTransform *
00104 append_color_change(Gt_ColorTransform *list,
00105                     Gif_Color old_color, Gif_Color new_color)
00106 {
00107   Gt_ColorTransform *xform;
00108   Gt_ColorChange *change = Gif_New(Gt_ColorChange);
00109   change->old_color = old_color;
00110   change->new_color = new_color;
00111   change->next = 0;
00112   
00113   for (xform = list; xform && xform->next; xform = xform->next)
00114     ;
00115   if (!xform || xform->func != &color_change_transformer)
00116     return append_color_transform(list, &color_change_transformer, change);
00117   else {
00118     Gt_ColorChange *prev = (Gt_ColorChange *)(xform->data);
00119     while (prev->next) prev = prev->next;
00120     prev->next = change;
00121     return list;
00122   }
00123 }
00124 
00125 
00126 void
00127 pipe_color_transformer(Gif_Colormap *gfcm, void *thunk)
00128 {
00129   int i, status;
00130   FILE *f;
00131   Gif_Color *col = gfcm->col;
00132   Gif_Colormap *new_cm = 0;
00133   char *command = (char *)thunk;
00134   char *tmp_file = tmpnam(0);
00135   char *new_command;
00136   
00137   if (!tmp_file)
00138     fatal_error("can't create temporary file!");
00139   
00140   new_command = Gif_NewArray(char, strlen(command) + strlen(tmp_file) + 4);
00141   sprintf(new_command, "%s  >%s", command, tmp_file);
00142   f = popen(new_command, "w");
00143   if (!f)
00144     fatal_error("can't run color transformation command: %s", strerror(errno));
00145   Gif_DeleteArray(new_command);
00146   
00147   for (i = 0; i < gfcm->ncol; i++)
00148     fprintf(f, "%d %d %d\n", col[i].red, col[i].green, col[i].blue);
00149   
00150   errno = 0;
00151   status = pclose(f);
00152   if (status < 0) {
00153     error("color transformation error: %s", strerror(errno));
00154     goto done;
00155   } else if (status > 0) {
00156     error("color transformation command failed");
00157     goto done;
00158   }
00159   
00160   f = fopen(tmp_file, "r");
00161   if (!f || feof(f)) {
00162     error("color transformation command generated no output", command);
00163     if (f) fclose(f);
00164     goto done;
00165   }
00166   new_cm = read_colormap_file("<color transformation>", f);
00167   fclose(f);
00168   
00169   if (new_cm) {
00170     int nc = new_cm->ncol;
00171     if (nc < gfcm->ncol) {
00172       nc = gfcm->ncol;
00173       warning("too few colors in color transformation results");
00174     } else if (nc > gfcm->ncol)
00175       warning("too many colors in color transformation results");
00176     for (i = 0; i < nc; i++)
00177       col[i] = new_cm->col[i];
00178   }
00179   
00180  done:
00181   remove(tmp_file);
00182   Gif_DeleteColormap(new_cm);
00183 }
00184 
00185 
00186 
00187 
00188 
00189 
00190 
00191 int
00192 crop_image(Gif_Image *gfi, Gt_Crop *crop)
00193 {
00194   int x, y, w, h, j;
00195   byte **img;
00196   
00197   if (!crop->ready) {
00198     crop->x = crop->spec_x + gfi->left;
00199     crop->y = crop->spec_y + gfi->top;
00200     crop->w = crop->spec_w <= 0 ? gfi->width + crop->spec_w : crop->spec_w;
00201     crop->h = crop->spec_h <= 0 ? gfi->height + crop->spec_h : crop->spec_h;
00202     if (crop->x < 0 || crop->y < 0 || crop->w <= 0 || crop->h <= 0
00203         || crop->x + crop->w > gfi->width
00204         || crop->y + crop->h > gfi->height) {
00205       error("cropping dimensions don't fit image");
00206       crop->ready = 2;
00207     } else
00208       crop->ready = 1;
00209   }
00210   if (crop->ready == 2)
00211     return 1;
00212   
00213   x = crop->x - gfi->left;
00214   y = crop->y - gfi->top;
00215   w = crop->w;
00216   h = crop->h;
00217   
00218   
00219   if (x < 0) w += x, x = 0;
00220   if (y < 0) h += y, y = 0;
00221   if (x + w > gfi->width) w = gfi->width - x;
00222   if (y + h > gfi->height) h = gfi->height - y;
00223   
00224   if (w > 0 && h > 0) {
00225     img = Gif_NewArray(byte *, h + 1);
00226     for (j = 0; j < h; j++)
00227       img[j] = gfi->img[y + j] + x;
00228     img[h] = 0;
00229     
00230     
00231     if (crop->whole_stream) {
00232       
00233 
00234       crop->left_off = x + gfi->left;
00235       crop->right_off = y + gfi->top;
00236       gfi->left = 0;
00237       gfi->top = 0;
00238       crop->whole_stream = 0;
00239     } else {
00240       gfi->left += x - crop->left_off;
00241       gfi->top += y - crop->right_off;
00242     }
00243     
00244   } else {
00245     
00246     w = h = 0;
00247     img = 0;
00248   }
00249   
00250   Gif_DeleteArray(gfi->img);
00251   gfi->img = img;
00252   gfi->width = w;
00253   gfi->height = h;
00254   return gfi->img != 0;
00255 }
00256 
00257 
00258 
00259 
00260 
00261 
00262 void
00263 flip_image(Gif_Image *gfi, int screen_width, int screen_height, int is_vert)
00264 {
00265   int x, y;
00266   int width = gfi->width;
00267   int height = gfi->height;
00268   byte **img = gfi->img;
00269   
00270   
00271   if (!is_vert) {
00272     byte *buffer = Gif_NewArray(byte, width);
00273     byte *trav;
00274     for (y = 0; y < height; y++) {
00275       memcpy(buffer, img[y], width);
00276       trav = img[y] + width - 1;
00277       for (x = 0; x < width; x++)
00278         *trav-- = buffer[x];
00279     }
00280     gfi->left = screen_width - (gfi->left + width);
00281     Gif_DeleteArray(buffer);
00282   }
00283   
00284   
00285   if (is_vert) {
00286     byte **buffer = Gif_NewArray(byte *, height);
00287     memcpy(buffer, img, height * sizeof(byte *));
00288     for (y = 0; y < height; y++)
00289       img[y] = buffer[height - y - 1];
00290     gfi->top = screen_height - (gfi->top + height);
00291     Gif_DeleteArray(buffer);
00292   }
00293 }
00294 
00295 void
00296 rotate_image(Gif_Image *gfi, int screen_width, int screen_height, int rotation)
00297 {
00298   int x, y;
00299   int width = gfi->width;
00300   int height = gfi->height;
00301   byte **img = gfi->img;
00302   byte *new_data = Gif_NewArray(byte, width * height);
00303   byte *trav = new_data;
00304   
00305   
00306   assert(rotation == 1 || rotation == 3);
00307   
00308   if (rotation == 1) {
00309     for (x = 0; x < width; x++)
00310       for (y = height - 1; y >= 0; y--)
00311         *trav++ = img[y][x];
00312     x = gfi->left;
00313     gfi->left = screen_height - (gfi->top + height);
00314     gfi->top = x;
00315     
00316   } else {
00317     for (x = width - 1; x >= 0; x--)
00318       for (y = 0; y < height; y++)
00319         *trav++ = img[y][x];
00320     y = gfi->top;
00321     gfi->top = screen_width - (gfi->left + width);
00322     gfi->left = y;
00323   }
00324   
00325   Gif_ReleaseUncompressedImage(gfi);
00326   gfi->width = height;
00327   gfi->height = width;
00328   Gif_SetUncompressedImage(gfi, new_data, Gif_DeleteArrayFunc, 0);
00329 }
00330 
00331 
00332 
00333 
00334 
00335 
00336 #define SCALE(d)        ((d) << 10)
00337 #define UNSCALE(d)      ((d) >> 10)
00338 #define SCALE_FACTOR    SCALE(1)
00339 
00340 void
00341 scale_image(Gif_Stream *gfs, Gif_Image *gfi, double xfactor, double yfactor)
00342 {
00343   byte *new_data;
00344   int new_left, new_top, new_right, new_bottom, new_width, new_height;
00345   
00346   int i, j, new_x, new_y;
00347   int scaled_xstep, scaled_ystep, scaled_new_x, scaled_new_y;
00348 
00349   
00350 
00351 
00352 
00353   
00354   
00355   scaled_xstep = (int)(SCALE_FACTOR * xfactor + 0.5);
00356   scaled_ystep = (int)(SCALE_FACTOR * yfactor + 0.5);
00357   
00358   
00359 
00360 
00361 
00362 
00363   new_left = UNSCALE(scaled_xstep * gfi->left);
00364   new_top = UNSCALE(scaled_ystep * gfi->top);
00365   new_right = UNSCALE(scaled_xstep * (gfi->left + gfi->width));
00366   new_bottom = UNSCALE(scaled_ystep * (gfi->top + gfi->height));
00367   
00368   new_width = new_right - new_left;
00369   new_height = new_bottom - new_top;
00370 
00371   if (new_width <= 0) new_width = 1, new_right = new_left + 1;
00372   if (new_height <= 0) new_height = 1, new_bottom = new_top + 1;
00373   if (new_width > UNSCALE(INT_MAX) || new_height > UNSCALE(INT_MAX))
00374     fatal_error("new image size is too big for me to handle");
00375   
00376   assert(gfi->img);
00377   new_data = Gif_NewArray(byte, new_width * new_height);
00378   
00379   new_y = new_top;
00380   scaled_new_y = scaled_ystep * gfi->top;
00381   
00382   for (j = 0; j < gfi->height; j++) {
00383     byte *in_line = gfi->img[j];
00384     byte *out_data;
00385     int x_delta, y_delta, yinc;
00386     
00387     scaled_new_y += scaled_ystep;
00388     
00389     if (j == gfi->height - 1) scaled_new_y = SCALE(new_bottom);
00390     
00391     if (scaled_new_y < SCALE(new_y + 1)) continue;
00392     y_delta = UNSCALE(scaled_new_y - SCALE(new_y));
00393     
00394     new_x = new_left;
00395     scaled_new_x = scaled_xstep * gfi->left;
00396     out_data = &new_data[(new_y - new_top) * new_width + (new_x - new_left)];
00397     
00398     for (i = 0; i < gfi->width; i++) {
00399       scaled_new_x += scaled_xstep;
00400       
00401       if (i == gfi->width - 1) scaled_new_x = SCALE(new_right);
00402       
00403       x_delta = UNSCALE(scaled_new_x - SCALE(new_x));
00404       
00405       for (; x_delta > 0; new_x++, x_delta--, out_data++)
00406         for (yinc = 0; yinc < y_delta; yinc++)
00407           out_data[yinc * new_width] = in_line[i];
00408     }
00409     
00410     new_y += y_delta;
00411   }
00412   
00413   Gif_ReleaseUncompressedImage(gfi);
00414   Gif_ReleaseCompressedImage(gfi);
00415   gfi->width = new_width;
00416   gfi->height = new_height;
00417   gfi->left = UNSCALE(scaled_xstep * gfi->left);
00418   gfi->top = UNSCALE(scaled_ystep * gfi->top);
00419   Gif_SetUncompressedImage(gfi, new_data, Gif_DeleteArrayFunc, 0);
00420 }
00421 
00422 void
00423 resize_stream(Gif_Stream *gfs, int new_width, int new_height)
00424 {
00425   double xfactor, yfactor;
00426   int i;
00427 
00428   if (new_width <= 0)
00429     new_width = (int)(((double)gfs->screen_width / gfs->screen_height) * new_height);
00430   if (new_height <= 0)
00431     new_height = (int)(((double)gfs->screen_height / gfs->screen_width) * new_width);
00432   
00433   Gif_CalculateScreenSize(gfs, 0);
00434   xfactor = (double)new_width / gfs->screen_width;
00435   yfactor = (double)new_height / gfs->screen_height;
00436   for (i = 0; i < gfs->nimages; i++)
00437     scale_image(gfs, gfs->images[i], xfactor, yfactor);
00438   
00439   gfs->screen_width = new_width;
00440   gfs->screen_height = new_height;
00441 }