1 /*****************************************************************************
2
3 gifclrmap - extract colormaps from GIF images
4
5 SPDX-License-Identifier: MIT
6
7 *****************************************************************************/
8
9 #include <assert.h>
10 #include <ctype.h>
11 #include <math.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include "getarg.h"
18 #include "gif_lib.h"
19
20 #define PROGRAM_NAME "gifclrmp"
21
22 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
23 " Gershon Elber, " __DATE__ ", " __TIME__ "\n"
24 "(C) Copyright 1989 Gershon Elber.\n";
25 static char *CtrlStr =
26 PROGRAM_NAME " v%- s%- t%-TranslationFile!s l%-ColorMapFile!s g%-Gamma!F "
27 "i%-Image#!d h%- GifFile!*s";
28
29 static bool SaveFlag = false, TranslateFlag = false, LoadFlag = false,
30 GammaFlag = false;
31 static double Gamma = 1.0;
32 static FILE *ColorFile = NULL;
33 FILE *TranslateFile = NULL;
34 static GifPixelType Translation[256];
35
36 static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap);
37 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut);
38
39 /******************************************************************************
40 Interpret the command line and scan the given GIF file.
41 ******************************************************************************/
main(int argc,char ** argv)42 int main(int argc, char **argv) {
43 int NumFiles, ExtCode, CodeSize, ImageNum = 0, ImageN, HasGIFOutput,
44 ErrorCode;
45 bool Error, ImageNFlag = false, HelpFlag = false, GifNoisyPrint = false;
46 GifRecordType RecordType;
47 GifByteType *Extension, *CodeBlock;
48 char **FileName = NULL, *ColorFileName, *TranslateFileName;
49 GifFileType *GifFileIn = NULL, *GifFileOut = NULL;
50
51 if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &SaveFlag,
52 &TranslateFlag, &TranslateFileName, &LoadFlag,
53 &ColorFileName, &GammaFlag, &Gamma, &ImageNFlag,
54 &ImageN, &HelpFlag, &NumFiles, &FileName)) !=
55 false ||
56 (NumFiles > 1 && !HelpFlag)) {
57 if (Error) {
58 GAPrintErrMsg(Error);
59 } else if (NumFiles > 1) {
60 GIF_MESSAGE("Error in command line parsing - one GIF "
61 "file please.");
62 }
63 GAPrintHowTo(CtrlStr);
64 exit(EXIT_FAILURE);
65 }
66
67 if (HelpFlag) {
68 (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
69 GAPrintHowTo(CtrlStr);
70 exit(EXIT_SUCCESS);
71 }
72
73 if (SaveFlag + LoadFlag + GammaFlag + TranslateFlag > 1) {
74 GIF_EXIT("Can not handle more than one of -s -l, -t, or -g at "
75 "the same time.");
76 }
77
78 /* Default action is to dump colormaps */
79 if (!SaveFlag && !LoadFlag && !GammaFlag && !TranslateFlag) {
80 SaveFlag = true;
81 }
82
83 if (NumFiles == 1) {
84 if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) ==
85 NULL) {
86 PrintGifError(ErrorCode);
87 exit(EXIT_FAILURE);
88 }
89 } else {
90 /* Use stdin instead: */
91 if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
92 PrintGifError(ErrorCode);
93 exit(EXIT_FAILURE);
94 }
95 }
96
97 if (SaveFlag) {
98 /* We are dumping out the color map as text file to stdout: */
99 ColorFile = stdout;
100 } else {
101 if (TranslateFlag) {
102 /* We are loading new color map from specified file: */
103 if ((TranslateFile = fopen(TranslateFileName, "rt")) ==
104 NULL) {
105 GIF_EXIT("Failed to open specified color "
106 "translation file.");
107 }
108 }
109
110 if (LoadFlag) {
111 /* We are loading new color map from specified file: */
112 if ((ColorFile = fopen(ColorFileName, "rt")) == NULL) {
113 GIF_EXIT(
114 "Failed to open specified color map file.");
115 }
116 }
117 }
118
119 if ((HasGIFOutput = (LoadFlag || TranslateFlag || GammaFlag)) != 0) {
120 /* Open stdout for GIF output file: */
121 if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
122 PrintGifError(ErrorCode);
123 exit(EXIT_FAILURE);
124 }
125 }
126
127 if (!ImageNFlag) {
128 /* We are supposed to modify the screen color map, so do it: */
129 if (!GifFileIn->SColorMap) {
130 GIF_EXIT("No colormap to modify");
131 }
132 GifFileIn->SColorMap = ModifyColorMap(GifFileIn->SColorMap);
133 if (!HasGIFOutput) {
134 /* We can quit here, as we have the color map: */
135 DGifCloseFile(GifFileIn, NULL);
136 fclose(ColorFile);
137 exit(EXIT_SUCCESS);
138 }
139 }
140 /* And dump out its new possible repositioned screen information: */
141 if (HasGIFOutput) {
142 if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth,
143 GifFileIn->SHeight,
144 GifFileIn->SColorResolution,
145 GifFileIn->SBackGroundColor,
146 GifFileIn->SColorMap) == GIF_ERROR) {
147 QuitGifError(GifFileIn, GifFileOut);
148 }
149 }
150
151 /* Scan the content of the GIF file and load the image(s) in: */
152 do {
153 if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) {
154 QuitGifError(GifFileIn, GifFileOut);
155 }
156
157 switch (RecordType) {
158 case IMAGE_DESC_RECORD_TYPE:
159 if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) {
160 QuitGifError(GifFileIn, GifFileOut);
161 }
162 if ((++ImageNum == ImageN) && ImageNFlag) {
163 /* We are suppose to modify this image color
164 * map, do it: */
165 GifFileIn->SColorMap =
166 ModifyColorMap(GifFileIn->SColorMap);
167 if (!HasGIFOutput) {
168 /* We can quit here, as we have the
169 * color map: */
170 DGifCloseFile(GifFileIn, NULL);
171 fclose(ColorFile);
172 exit(EXIT_SUCCESS);
173 }
174 }
175 if (HasGIFOutput) {
176 if (EGifPutImageDesc(
177 GifFileOut, GifFileIn->Image.Left,
178 GifFileIn->Image.Top,
179 GifFileIn->Image.Width,
180 GifFileIn->Image.Height,
181 GifFileIn->Image.Interlace,
182 GifFileIn->Image.ColorMap) ==
183 GIF_ERROR) {
184 QuitGifError(GifFileIn, GifFileOut);
185 }
186 }
187
188 if (!TranslateFlag ||
189 (ImageNFlag && (ImageN != ImageNum))) {
190 /* Now read image itself in decoded form as we
191 * don't */
192 /* really care what we have there, and this is
193 * much */
194 /* faster.
195 */
196 if (DGifGetCode(GifFileIn, &CodeSize,
197 &CodeBlock) == GIF_ERROR) {
198 QuitGifError(GifFileIn, GifFileOut);
199 }
200 if (HasGIFOutput) {
201 if (EGifPutCode(GifFileOut, CodeSize,
202 CodeBlock) ==
203 GIF_ERROR) {
204 QuitGifError(GifFileIn,
205 GifFileOut);
206 }
207 }
208 while (CodeBlock != NULL) {
209 if (DGifGetCodeNext(GifFileIn,
210 &CodeBlock) ==
211 GIF_ERROR) {
212 QuitGifError(GifFileIn,
213 GifFileOut);
214 }
215 if (HasGIFOutput) {
216 if (EGifPutCodeNext(
217 GifFileOut,
218 CodeBlock) ==
219 GIF_ERROR) {
220 QuitGifError(
221 GifFileIn,
222 GifFileOut);
223 }
224 }
225 }
226 } else /* we need to mung pixels intices */
227 {
228 int i;
229 register GifPixelType *cp;
230
231 GifPixelType *Line = (GifPixelType *)malloc(
232 GifFileIn->Image.Width *
233 sizeof(GifPixelType));
234 for (i = 0; i < GifFileIn->Image.Height; i++) {
235 if (DGifGetLine(
236 GifFileIn, Line,
237 GifFileIn->Image.Width) ==
238 GIF_ERROR) {
239 QuitGifError(GifFileIn,
240 GifFileOut);
241 }
242
243 /* translation step goes here */
244 for (cp = Line;
245 cp < Line + GifFileIn->Image.Width;
246 cp++) {
247 *cp = Translation[*cp];
248 }
249
250 if (EGifPutLine(
251 GifFileOut, Line,
252 GifFileIn->Image.Width) ==
253 GIF_ERROR) {
254 QuitGifError(GifFileIn,
255 GifFileOut);
256 }
257 }
258 free((char *)Line);
259 }
260 break;
261 case EXTENSION_RECORD_TYPE:
262 assert(GifFileOut != NULL); /* might pacify Coverity */
263 /* pass through extension records */
264 if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) ==
265 GIF_ERROR) {
266 QuitGifError(GifFileIn, GifFileOut);
267 }
268 if (Extension == NULL) {
269 break;
270 }
271 if (EGifPutExtensionLeader(GifFileOut, ExtCode) ==
272 GIF_ERROR) {
273 QuitGifError(GifFileIn, GifFileOut);
274 }
275 if (EGifPutExtensionBlock(GifFileOut, Extension[0],
276 Extension + 1) == GIF_ERROR) {
277 QuitGifError(GifFileIn, GifFileOut);
278 }
279 while (Extension != NULL) {
280 if (DGifGetExtensionNext(
281 GifFileIn, &Extension) == GIF_ERROR) {
282 QuitGifError(GifFileIn, GifFileOut);
283 }
284 if (Extension != NULL) {
285 if (EGifPutExtensionBlock(
286 GifFileOut, Extension[0],
287 Extension + 1) == GIF_ERROR) {
288 QuitGifError(GifFileIn,
289 GifFileOut);
290 }
291 }
292 }
293 if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) {
294 QuitGifError(GifFileIn, GifFileOut);
295 }
296 break;
297 case TERMINATE_RECORD_TYPE:
298 break;
299 default: /* Should be trapped by DGifGetRecordType. */
300 break;
301 }
302 } while (RecordType != TERMINATE_RECORD_TYPE);
303
304 if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) {
305 PrintGifError(ErrorCode);
306 exit(EXIT_FAILURE);
307 }
308 if (HasGIFOutput) {
309 if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) {
310 PrintGifError(ErrorCode);
311 exit(EXIT_FAILURE);
312 }
313 }
314
315 return 0;
316 }
317
318 /******************************************************************************
319 Modify the given colormap according to global variables setting.
320 ******************************************************************************/
ModifyColorMap(ColorMapObject * ColorMap)321 static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap) {
322 int i, Dummy, Red, Green, Blue;
323
324 if (SaveFlag) {
325 /* Save this color map to ColorFile: */
326 for (i = 0; i < ColorMap->ColorCount; i++) {
327 fprintf(ColorFile, "%3d %3d %3d %3d\n", i,
328 ColorMap->Colors[i].Red,
329 ColorMap->Colors[i].Green,
330 ColorMap->Colors[i].Blue);
331 }
332 return (ColorMap);
333 } else if (LoadFlag) {
334 /* Read the color map in ColorFile into this color map: */
335 for (i = 0; i < ColorMap->ColorCount; i++) {
336 if (feof(ColorFile)) {
337 GIF_EXIT("Color file to load color map from, "
338 "too small.");
339 }
340 if (fscanf(ColorFile, "%3d %3d %3d %3d\n", &Dummy, &Red,
341 &Green, &Blue) == 4) {
342 ColorMap->Colors[i].Red = Red;
343 ColorMap->Colors[i].Green = Green;
344 ColorMap->Colors[i].Blue = Blue;
345 }
346 }
347 return (ColorMap);
348 } else if (GammaFlag) {
349 /* Apply gamma correction to this color map: */
350 double Gamma1 = 1.0 / Gamma;
351 for (i = 0; i < ColorMap->ColorCount; i++) {
352 ColorMap->Colors[i].Red =
353 ((int)(255 * pow(ColorMap->Colors[i].Red / 255.0,
354 Gamma1)));
355 ColorMap->Colors[i].Green =
356 ((int)(255 * pow(ColorMap->Colors[i].Green / 255.0,
357 Gamma1)));
358 ColorMap->Colors[i].Blue =
359 ((int)(255 * pow(ColorMap->Colors[i].Blue / 255.0,
360 Gamma1)));
361 }
362 return (ColorMap);
363 } else if (TranslateFlag) {
364 ColorMapObject *NewMap;
365 int Max = 0;
366
367 /* Read the translation table in TranslateFile: */
368 for (i = 0; i < ColorMap->ColorCount; i++) {
369 int tmp;
370 if (feof(TranslateFile)) {
371 GIF_EXIT("Color file to load color map from, "
372 "too small.");
373 }
374 if (fscanf(TranslateFile, "%3d %3d\n", &Dummy, &tmp) ==
375 2) {
376 Translation[i] = tmp & 0xff;
377 if (Translation[i] > Max) {
378 Max = Translation[i];
379 }
380 }
381 }
382
383 if ((NewMap = GifMakeMapObject(1 << GifBitSize(Max + 1),
384 NULL)) == NULL) {
385 GIF_EXIT("Out of memory while allocating color map!");
386 }
387
388 /* Apply the translation; we'll do it to the pixels, too */
389 for (i = 0; i < ColorMap->ColorCount; i++) {
390 NewMap->Colors[i] = ColorMap->Colors[Translation[i]];
391 }
392
393 return (NewMap);
394 } else {
395 GIF_EXIT("Nothing to do!");
396 return (ColorMap);
397 }
398 }
399
400 /******************************************************************************
401 Close both input and output file (if open), and exit.
402 ******************************************************************************/
QuitGifError(GifFileType * GifFileIn,GifFileType * GifFileOut)403 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) {
404 if (GifFileIn != NULL) {
405 PrintGifError(GifFileIn->Error);
406 EGifCloseFile(GifFileIn, NULL);
407 }
408 if (GifFileOut != NULL) {
409 PrintGifError(GifFileOut->Error);
410 EGifCloseFile(GifFileOut, NULL);
411 }
412 exit(EXIT_FAILURE);
413 }
414
415 /* end */
416