1 /* xform.c - Image transformation functions for gifsicle.
2 Copyright (C) 1997-9 Eddie Kohler, eddietwo@lcs.mit.edu
3 This file is part of gifsicle.
4
5 Gifsicle is free software. It is distributed under the GNU Public License,
6 version 2 or later; you can copy, distribute, or alter it at will, as long
7 as this notice is kept intact and this source code is made available. There
8 is no warranty, express or implied. */
9
10 #include "config.h"
11 #include "gifsicle.h"
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <assert.h>
16 #include <errno.h>
17 #include <limits.h>
18
19
20 /******
21 * color transforms
22 **/
23
24 Gt_ColorTransform *
append_color_transform(Gt_ColorTransform * list,color_transform_func func,void * data)25 append_color_transform(Gt_ColorTransform *list,
26 color_transform_func func, void *data)
27 {
28 Gt_ColorTransform *trav;
29 Gt_ColorTransform *xform = Gif_New(Gt_ColorTransform);
30 xform->next = 0;
31 xform->func = func;
32 xform->data = data;
33
34 for (trav = list; trav && trav->next; trav = trav->next)
35 ;
36 if (trav) {
37 trav->next = xform;
38 return list;
39 } else
40 return xform;
41 }
42
43 Gt_ColorTransform *
delete_color_transforms(Gt_ColorTransform * list,color_transform_func func)44 delete_color_transforms(Gt_ColorTransform *list, color_transform_func func)
45 {
46 Gt_ColorTransform *prev = 0, *trav = list;
47 while (trav) {
48 Gt_ColorTransform *next = trav->next;
49 if (trav->func == func) {
50 if (prev) prev->next = next;
51 else list = next;
52 Gif_Delete(trav);
53 } else
54 prev = trav;
55 trav = next;
56 }
57 return list;
58 }
59
60 void
apply_color_transforms(Gt_ColorTransform * list,Gif_Stream * gfs)61 apply_color_transforms(Gt_ColorTransform *list, Gif_Stream *gfs)
62 {
63 int i;
64 Gt_ColorTransform *xform;
65 for (xform = list; xform; xform = xform->next) {
66 if (gfs->global)
67 xform->func(gfs->global, xform->data);
68 for (i = 0; i < gfs->nimages; i++)
69 if (gfs->images[i]->local)
70 xform->func(gfs->images[i]->local, xform->data);
71 }
72 }
73
74
75 typedef struct Gt_ColorChange {
76 struct Gt_ColorChange *next;
77 Gif_Color old_color;
78 Gif_Color new_color;
79 } Gt_ColorChange;
80
81 void
color_change_transformer(Gif_Colormap * gfcm,void * thunk)82 color_change_transformer(Gif_Colormap *gfcm, void *thunk)
83 {
84 int i, have;
85 Gt_ColorChange *first_change = (Gt_ColorChange *)thunk;
86 Gt_ColorChange *change;
87
88 /* change colors named by color */
89 for (i = 0; i < gfcm->ncol; i++)
90 for (change = first_change; change; change = change->next) {
91 if (!change->old_color.haspixel)
92 have = GIF_COLOREQ(&gfcm->col[i], &change->old_color);
93 else
94 have = change->old_color.pixel == i;
95
96 if (have) {
97 gfcm->col[i] = change->new_color;
98 break; /* ignore remaining color changes */
99 }
100 }
101 }
102
103 Gt_ColorTransform *
append_color_change(Gt_ColorTransform * list,Gif_Color old_color,Gif_Color new_color)104 append_color_change(Gt_ColorTransform *list,
105 Gif_Color old_color, Gif_Color new_color)
106 {
107 Gt_ColorTransform *xform;
108 Gt_ColorChange *change = Gif_New(Gt_ColorChange);
109 change->old_color = old_color;
110 change->new_color = new_color;
111 change->next = 0;
112
113 for (xform = list; xform && xform->next; xform = xform->next)
114 ;
115 if (!xform || xform->func != &color_change_transformer)
116 return append_color_transform(list, &color_change_transformer, change);
117 else {
118 Gt_ColorChange *prev = (Gt_ColorChange *)(xform->data);
119 while (prev->next) prev = prev->next;
120 prev->next = change;
121 return list;
122 }
123 }
124
125
126 void
pipe_color_transformer(Gif_Colormap * gfcm,void * thunk)127 pipe_color_transformer(Gif_Colormap *gfcm, void *thunk)
128 {
129 int i, status;
130 FILE *f;
131 Gif_Color *col = gfcm->col;
132 Gif_Colormap *new_cm = 0;
133 char *command = (char *)thunk;
134 char *tmp_file = tmpnam(0);
135 char *new_command;
136
137 if (!tmp_file)
138 fatal_error("can't create temporary file!");
139
140 new_command = Gif_NewArray(char, strlen(command) + strlen(tmp_file) + 4);
141 sprintf(new_command, "%s >%s", command, tmp_file);
142 f = popen(new_command, "w");
143 if (!f)
144 fatal_error("can't run color transformation command: %s", strerror(errno));
145 Gif_DeleteArray(new_command);
146
147 for (i = 0; i < gfcm->ncol; i++)
148 fprintf(f, "%d %d %d\n", col[i].red, col[i].green, col[i].blue);
149
150 errno = 0;
151 status = pclose(f);
152 if (status < 0) {
153 error("color transformation error: %s", strerror(errno));
154 goto done;
155 } else if (status > 0) {
156 error("color transformation command failed");
157 goto done;
158 }
159
160 f = fopen(tmp_file, "r");
161 if (!f || feof(f)) {
162 error("color transformation command generated no output", command);
163 if (f) fclose(f);
164 goto done;
165 }
166 new_cm = read_colormap_file("<color transformation>", f);
167 fclose(f);
168
169 if (new_cm) {
170 int nc = new_cm->ncol;
171 if (nc < gfcm->ncol) {
172 nc = gfcm->ncol;
173 warning("too few colors in color transformation results");
174 } else if (nc > gfcm->ncol)
175 warning("too many colors in color transformation results");
176 for (i = 0; i < nc; i++)
177 col[i] = new_cm->col[i];
178 }
179
180 done:
181 remove(tmp_file);
182 Gif_DeleteColormap(new_cm);
183 }
184
185
186
187 /*****
188 * crop image; returns true if the image exists
189 **/
190
191 int
crop_image(Gif_Image * gfi,Gt_Crop * crop)192 crop_image(Gif_Image *gfi, Gt_Crop *crop)
193 {
194 int x, y, w, h, j;
195 byte **img;
196
197 if (!crop->ready) {
198 crop->x = crop->spec_x + gfi->left;
199 crop->y = crop->spec_y + gfi->top;
200 crop->w = crop->spec_w <= 0 ? gfi->width + crop->spec_w : crop->spec_w;
201 crop->h = crop->spec_h <= 0 ? gfi->height + crop->spec_h : crop->spec_h;
202 if (crop->x < 0 || crop->y < 0 || crop->w <= 0 || crop->h <= 0
203 || crop->x + crop->w > gfi->width
204 || crop->y + crop->h > gfi->height) {
205 error("cropping dimensions don't fit image");
206 crop->ready = 2;
207 } else
208 crop->ready = 1;
209 }
210 if (crop->ready == 2)
211 return 1;
212
213 x = crop->x - gfi->left;
214 y = crop->y - gfi->top;
215 w = crop->w;
216 h = crop->h;
217
218 /* Check that the rectangle actually intersects with the image. */
219 if (x < 0) w += x, x = 0;
220 if (y < 0) h += y, y = 0;
221 if (x + w > gfi->width) w = gfi->width - x;
222 if (y + h > gfi->height) h = gfi->height - y;
223
224 if (w > 0 && h > 0) {
225 img = Gif_NewArray(byte *, h + 1);
226 for (j = 0; j < h; j++)
227 img[j] = gfi->img[y + j] + x;
228 img[h] = 0;
229
230 /* Change position of image appropriately */
231 if (crop->whole_stream) {
232 /* If cropping the whole stream, then this is the first frame. Position
233 it at (0,0). */
234 crop->left_off = x + gfi->left;
235 crop->right_off = y + gfi->top;
236 gfi->left = 0;
237 gfi->top = 0;
238 crop->whole_stream = 0;
239 } else {
240 gfi->left += x - crop->left_off;
241 gfi->top += y - crop->right_off;
242 }
243
244 } else {
245 /* Empty image */
246 w = h = 0;
247 img = 0;
248 }
249
250 Gif_DeleteArray(gfi->img);
251 gfi->img = img;
252 gfi->width = w;
253 gfi->height = h;
254 return gfi->img != 0;
255 }
256
257
258 /*****
259 * flip and rotate
260 **/
261
262 void
flip_image(Gif_Image * gfi,int screen_width,int screen_height,int is_vert)263 flip_image(Gif_Image *gfi, int screen_width, int screen_height, int is_vert)
264 {
265 int x, y;
266 int width = gfi->width;
267 int height = gfi->height;
268 byte **img = gfi->img;
269
270 /* horizontal flips */
271 if (!is_vert) {
272 byte *buffer = Gif_NewArray(byte, width);
273 byte *trav;
274 for (y = 0; y < height; y++) {
275 memcpy(buffer, img[y], width);
276 trav = img[y] + width - 1;
277 for (x = 0; x < width; x++)
278 *trav-- = buffer[x];
279 }
280 gfi->left = screen_width - (gfi->left + width);
281 Gif_DeleteArray(buffer);
282 }
283
284 /* vertical flips */
285 if (is_vert) {
286 byte **buffer = Gif_NewArray(byte *, height);
287 memcpy(buffer, img, height * sizeof(byte *));
288 for (y = 0; y < height; y++)
289 img[y] = buffer[height - y - 1];
290 gfi->top = screen_height - (gfi->top + height);
291 Gif_DeleteArray(buffer);
292 }
293 }
294
295 void
rotate_image(Gif_Image * gfi,int screen_width,int screen_height,int rotation)296 rotate_image(Gif_Image *gfi, int screen_width, int screen_height, int rotation)
297 {
298 int x, y;
299 int width = gfi->width;
300 int height = gfi->height;
301 byte **img = gfi->img;
302 byte *new_data = Gif_NewArray(byte, width * height);
303 byte *trav = new_data;
304
305 /* this function can only rotate by 90 or 270 degrees */
306 assert(rotation == 1 || rotation == 3);
307
308 if (rotation == 1) {
309 for (x = 0; x < width; x++)
310 for (y = height - 1; y >= 0; y--)
311 *trav++ = img[y][x];
312 x = gfi->left;
313 gfi->left = screen_height - (gfi->top + height);
314 gfi->top = x;
315
316 } else {
317 for (x = width - 1; x >= 0; x--)
318 for (y = 0; y < height; y++)
319 *trav++ = img[y][x];
320 y = gfi->top;
321 gfi->top = screen_width - (gfi->left + width);
322 gfi->left = y;
323 }
324
325 Gif_ReleaseUncompressedImage(gfi);
326 gfi->width = height;
327 gfi->height = width;
328 Gif_SetUncompressedImage(gfi, new_data, Gif_DeleteArrayFunc, 0);
329 }
330
331
332 /*****
333 * scale
334 **/
335
336 #define SCALE(d) ((d) << 10)
337 #define UNSCALE(d) ((d) >> 10)
338 #define SCALE_FACTOR SCALE(1)
339
340 void
scale_image(Gif_Stream * gfs,Gif_Image * gfi,double xfactor,double yfactor)341 scale_image(Gif_Stream *gfs, Gif_Image *gfi, double xfactor, double yfactor)
342 {
343 byte *new_data;
344 int new_left, new_top, new_right, new_bottom, new_width, new_height;
345
346 int i, j, new_x, new_y;
347 int scaled_xstep, scaled_ystep, scaled_new_x, scaled_new_y;
348
349 /* Fri 9 Jan 1999: Fix problem with resizing animated GIFs: we scaled from
350 left edge of the *subimage* to right edge of the subimage, causing
351 consistency problems when several subimages overlap. Solution: always use
352 scale factors relating to the *whole image* (the screen size). */
353
354 /* use fixed-point arithmetic */
355 scaled_xstep = (int)(SCALE_FACTOR * xfactor + 0.5);
356 scaled_ystep = (int)(SCALE_FACTOR * yfactor + 0.5);
357
358 /* calculate new width and height based on the four edges (left, right, top,
359 bottom). This is better than simply multiplying the width and height by
360 the scale factors because it avoids roundoff inconsistencies between
361 frames on animated GIFs. Don't allow 0-width or 0-height images; GIF
362 doesn't support them well. */
363 new_left = UNSCALE(scaled_xstep * gfi->left);
364 new_top = UNSCALE(scaled_ystep * gfi->top);
365 new_right = UNSCALE(scaled_xstep * (gfi->left + gfi->width));
366 new_bottom = UNSCALE(scaled_ystep * (gfi->top + gfi->height));
367
368 new_width = new_right - new_left;
369 new_height = new_bottom - new_top;
370
371 if (new_width <= 0) new_width = 1, new_right = new_left + 1;
372 if (new_height <= 0) new_height = 1, new_bottom = new_top + 1;
373 if (new_width > UNSCALE(INT_MAX) || new_height > UNSCALE(INT_MAX))
374 fatal_error("new image size is too big for me to handle");
375
376 assert(gfi->img);
377 new_data = Gif_NewArray(byte, new_width * new_height);
378
379 new_y = new_top;
380 scaled_new_y = scaled_ystep * gfi->top;
381
382 for (j = 0; j < gfi->height; j++) {
383 byte *in_line = gfi->img[j];
384 byte *out_data;
385 int x_delta, y_delta, yinc;
386
387 scaled_new_y += scaled_ystep;
388 /* account for images which should've had 0 height but don't */
389 if (j == gfi->height - 1) scaled_new_y = SCALE(new_bottom);
390
391 if (scaled_new_y < SCALE(new_y + 1)) continue;
392 y_delta = UNSCALE(scaled_new_y - SCALE(new_y));
393
394 new_x = new_left;
395 scaled_new_x = scaled_xstep * gfi->left;
396 out_data = &new_data[(new_y - new_top) * new_width + (new_x - new_left)];
397
398 for (i = 0; i < gfi->width; i++) {
399 scaled_new_x += scaled_xstep;
400 /* account for images which should've had 0 width but don't */
401 if (i == gfi->width - 1) scaled_new_x = SCALE(new_right);
402
403 x_delta = UNSCALE(scaled_new_x - SCALE(new_x));
404
405 for (; x_delta > 0; new_x++, x_delta--, out_data++)
406 for (yinc = 0; yinc < y_delta; yinc++)
407 out_data[yinc * new_width] = in_line[i];
408 }
409
410 new_y += y_delta;
411 }
412
413 Gif_ReleaseUncompressedImage(gfi);
414 Gif_ReleaseCompressedImage(gfi);
415 gfi->width = new_width;
416 gfi->height = new_height;
417 gfi->left = UNSCALE(scaled_xstep * gfi->left);
418 gfi->top = UNSCALE(scaled_ystep * gfi->top);
419 Gif_SetUncompressedImage(gfi, new_data, Gif_DeleteArrayFunc, 0);
420 }
421
422 void
resize_stream(Gif_Stream * gfs,int new_width,int new_height)423 resize_stream(Gif_Stream *gfs, int new_width, int new_height)
424 {
425 double xfactor, yfactor;
426 int i;
427
428 if (new_width <= 0)
429 new_width = (int)(((double)gfs->screen_width / gfs->screen_height) * new_height);
430 if (new_height <= 0)
431 new_height = (int)(((double)gfs->screen_height / gfs->screen_width) * new_width);
432
433 Gif_CalculateScreenSize(gfs, 0);
434 xfactor = (double)new_width / gfs->screen_width;
435 yfactor = (double)new_height / gfs->screen_height;
436 for (i = 0; i < gfs->nimages; i++)
437 scale_image(gfs, gfs->images[i], xfactor, yfactor);
438
439 gfs->screen_width = new_width;
440 gfs->screen_height = new_height;
441 }
442