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 }