xref: /aosp_15_r20/external/giflib/gifclrmp.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
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