xref: /aosp_15_r20/external/giflib/giffix.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1*324bb76bSAndroid Build Coastguard Worker /*****************************************************************************
2*324bb76bSAndroid Build Coastguard Worker 
3*324bb76bSAndroid Build Coastguard Worker giffix - attempt to fix a truncated GIF
4*324bb76bSAndroid Build Coastguard Worker 
5*324bb76bSAndroid Build Coastguard Worker SPDX-License-Identifier: MIT
6*324bb76bSAndroid Build Coastguard Worker 
7*324bb76bSAndroid Build Coastguard Worker *****************************************************************************/
8*324bb76bSAndroid Build Coastguard Worker 
9*324bb76bSAndroid Build Coastguard Worker #include <ctype.h>
10*324bb76bSAndroid Build Coastguard Worker #include <stdbool.h>
11*324bb76bSAndroid Build Coastguard Worker #include <stdio.h>
12*324bb76bSAndroid Build Coastguard Worker #include <stdlib.h>
13*324bb76bSAndroid Build Coastguard Worker #include <string.h>
14*324bb76bSAndroid Build Coastguard Worker 
15*324bb76bSAndroid Build Coastguard Worker #include "getarg.h"
16*324bb76bSAndroid Build Coastguard Worker #include "gif_lib.h"
17*324bb76bSAndroid Build Coastguard Worker 
18*324bb76bSAndroid Build Coastguard Worker #define PROGRAM_NAME "giffix"
19*324bb76bSAndroid Build Coastguard Worker 
20*324bb76bSAndroid Build Coastguard Worker static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
21*324bb76bSAndroid Build Coastguard Worker     "	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
22*324bb76bSAndroid Build Coastguard Worker     "(C) Copyright 1989 Gershon Elber.\n";
23*324bb76bSAndroid Build Coastguard Worker static char *CtrlStr = PROGRAM_NAME " v%- h%- GifFile!*s";
24*324bb76bSAndroid Build Coastguard Worker 
25*324bb76bSAndroid Build Coastguard Worker static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut);
26*324bb76bSAndroid Build Coastguard Worker 
27*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
28*324bb76bSAndroid Build Coastguard Worker  Interpret the command line and scan the given GIF file.
29*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
main(int argc,char ** argv)30*324bb76bSAndroid Build Coastguard Worker int main(int argc, char **argv) {
31*324bb76bSAndroid Build Coastguard Worker 	int i, j, NumFiles, ExtCode, Row, Col, Width, Height, ErrorCode,
32*324bb76bSAndroid Build Coastguard Worker 	    DarkestColor = 0, ColorIntens = 10000;
33*324bb76bSAndroid Build Coastguard Worker 	bool Error, HelpFlag = false, GifNoisyPrint = false;
34*324bb76bSAndroid Build Coastguard Worker 	GifRecordType RecordType;
35*324bb76bSAndroid Build Coastguard Worker 	GifByteType *Extension;
36*324bb76bSAndroid Build Coastguard Worker 	char **FileName = NULL;
37*324bb76bSAndroid Build Coastguard Worker 	GifRowType LineBuffer;
38*324bb76bSAndroid Build Coastguard Worker 	ColorMapObject *ColorMap;
39*324bb76bSAndroid Build Coastguard Worker 	GifFileType *GifFileIn = NULL, *GifFileOut = NULL;
40*324bb76bSAndroid Build Coastguard Worker 	int ImageNum = 0;
41*324bb76bSAndroid Build Coastguard Worker 
42*324bb76bSAndroid Build Coastguard Worker 	if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &HelpFlag,
43*324bb76bSAndroid Build Coastguard Worker 	                       &NumFiles, &FileName)) != false ||
44*324bb76bSAndroid Build Coastguard Worker 	    (NumFiles > 1 && !HelpFlag)) {
45*324bb76bSAndroid Build Coastguard Worker 		if (Error) {
46*324bb76bSAndroid Build Coastguard Worker 			GAPrintErrMsg(Error);
47*324bb76bSAndroid Build Coastguard Worker 		} else if (NumFiles > 1) {
48*324bb76bSAndroid Build Coastguard Worker 			GIF_MESSAGE("Error in command line parsing - one GIF "
49*324bb76bSAndroid Build Coastguard Worker 			            "file please.");
50*324bb76bSAndroid Build Coastguard Worker 		}
51*324bb76bSAndroid Build Coastguard Worker 		GAPrintHowTo(CtrlStr);
52*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
53*324bb76bSAndroid Build Coastguard Worker 	}
54*324bb76bSAndroid Build Coastguard Worker 
55*324bb76bSAndroid Build Coastguard Worker 	if (HelpFlag) {
56*324bb76bSAndroid Build Coastguard Worker 		(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
57*324bb76bSAndroid Build Coastguard Worker 		GAPrintHowTo(CtrlStr);
58*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_SUCCESS);
59*324bb76bSAndroid Build Coastguard Worker 	}
60*324bb76bSAndroid Build Coastguard Worker 
61*324bb76bSAndroid Build Coastguard Worker 	if (NumFiles == 1) {
62*324bb76bSAndroid Build Coastguard Worker 		if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) ==
63*324bb76bSAndroid Build Coastguard Worker 		    NULL) {
64*324bb76bSAndroid Build Coastguard Worker 			PrintGifError(ErrorCode);
65*324bb76bSAndroid Build Coastguard Worker 			exit(EXIT_FAILURE);
66*324bb76bSAndroid Build Coastguard Worker 		}
67*324bb76bSAndroid Build Coastguard Worker 	} else {
68*324bb76bSAndroid Build Coastguard Worker 		/* Use stdin instead: */
69*324bb76bSAndroid Build Coastguard Worker 		if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
70*324bb76bSAndroid Build Coastguard Worker 			PrintGifError(ErrorCode);
71*324bb76bSAndroid Build Coastguard Worker 			exit(EXIT_FAILURE);
72*324bb76bSAndroid Build Coastguard Worker 		}
73*324bb76bSAndroid Build Coastguard Worker 	}
74*324bb76bSAndroid Build Coastguard Worker 
75*324bb76bSAndroid Build Coastguard Worker 	/* Open stdout for the output file: */
76*324bb76bSAndroid Build Coastguard Worker 	if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
77*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(ErrorCode);
78*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
79*324bb76bSAndroid Build Coastguard Worker 	}
80*324bb76bSAndroid Build Coastguard Worker 
81*324bb76bSAndroid Build Coastguard Worker 	/* Dump out exactly same screen information: */
82*324bb76bSAndroid Build Coastguard Worker 	/* coverity[var_deref_op] */
83*324bb76bSAndroid Build Coastguard Worker 	if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight,
84*324bb76bSAndroid Build Coastguard Worker 	                      GifFileIn->SColorResolution,
85*324bb76bSAndroid Build Coastguard Worker 	                      GifFileIn->SBackGroundColor,
86*324bb76bSAndroid Build Coastguard Worker 	                      GifFileIn->SColorMap) == GIF_ERROR) {
87*324bb76bSAndroid Build Coastguard Worker 		QuitGifError(GifFileIn, GifFileOut);
88*324bb76bSAndroid Build Coastguard Worker 	}
89*324bb76bSAndroid Build Coastguard Worker 
90*324bb76bSAndroid Build Coastguard Worker 	if ((LineBuffer = (GifRowType)malloc(GifFileIn->SWidth)) == NULL) {
91*324bb76bSAndroid Build Coastguard Worker 		GIF_EXIT("Failed to allocate memory required, aborted.");
92*324bb76bSAndroid Build Coastguard Worker 	}
93*324bb76bSAndroid Build Coastguard Worker 
94*324bb76bSAndroid Build Coastguard Worker 	/* Scan the content of the GIF file and load the image(s) in: */
95*324bb76bSAndroid Build Coastguard Worker 	do {
96*324bb76bSAndroid Build Coastguard Worker 		if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) {
97*324bb76bSAndroid Build Coastguard Worker 			QuitGifError(GifFileIn, GifFileOut);
98*324bb76bSAndroid Build Coastguard Worker 		}
99*324bb76bSAndroid Build Coastguard Worker 
100*324bb76bSAndroid Build Coastguard Worker 		switch (RecordType) {
101*324bb76bSAndroid Build Coastguard Worker 		case IMAGE_DESC_RECORD_TYPE:
102*324bb76bSAndroid Build Coastguard Worker 			if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) {
103*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFileIn, GifFileOut);
104*324bb76bSAndroid Build Coastguard Worker 			}
105*324bb76bSAndroid Build Coastguard Worker 			if (GifFileIn->Image.Interlace) {
106*324bb76bSAndroid Build Coastguard Worker 				GIF_EXIT("Cannot fix interlaced images.");
107*324bb76bSAndroid Build Coastguard Worker 			}
108*324bb76bSAndroid Build Coastguard Worker 
109*324bb76bSAndroid Build Coastguard Worker 			Row = GifFileIn->Image
110*324bb76bSAndroid Build Coastguard Worker 			          .Top; /* Image Position relative to Screen. */
111*324bb76bSAndroid Build Coastguard Worker 			Col = GifFileIn->Image.Left;
112*324bb76bSAndroid Build Coastguard Worker 			Width = GifFileIn->Image.Width;
113*324bb76bSAndroid Build Coastguard Worker 			Height = GifFileIn->Image.Height;
114*324bb76bSAndroid Build Coastguard Worker 			GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
115*324bb76bSAndroid Build Coastguard Worker 			           PROGRAM_NAME, ++ImageNum, Col, Row, Width,
116*324bb76bSAndroid Build Coastguard Worker 			           Height);
117*324bb76bSAndroid Build Coastguard Worker 			if (Width > GifFileIn->SWidth) {
118*324bb76bSAndroid Build Coastguard Worker 				GIF_EXIT("Image is wider than total");
119*324bb76bSAndroid Build Coastguard Worker 			}
120*324bb76bSAndroid Build Coastguard Worker 
121*324bb76bSAndroid Build Coastguard Worker 			/* Put the image descriptor to out file: */
122*324bb76bSAndroid Build Coastguard Worker 			if (EGifPutImageDesc(
123*324bb76bSAndroid Build Coastguard Worker 			        GifFileOut, Col, Row, Width, Height, false,
124*324bb76bSAndroid Build Coastguard Worker 			        GifFileIn->Image.ColorMap) == GIF_ERROR) {
125*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFileIn, GifFileOut);
126*324bb76bSAndroid Build Coastguard Worker 			}
127*324bb76bSAndroid Build Coastguard Worker 
128*324bb76bSAndroid Build Coastguard Worker 			/* Find the darkest color in color map to use as a
129*324bb76bSAndroid Build Coastguard Worker 			 * filler. */
130*324bb76bSAndroid Build Coastguard Worker 			ColorMap = (GifFileIn->Image.ColorMap
131*324bb76bSAndroid Build Coastguard Worker 			                ? GifFileIn->Image.ColorMap
132*324bb76bSAndroid Build Coastguard Worker 			                : GifFileIn->SColorMap);
133*324bb76bSAndroid Build Coastguard Worker 			for (i = 0; i < ColorMap->ColorCount; i++) {
134*324bb76bSAndroid Build Coastguard Worker 				j = ((int)ColorMap->Colors[i].Red) * 30 +
135*324bb76bSAndroid Build Coastguard Worker 				    ((int)ColorMap->Colors[i].Green) * 59 +
136*324bb76bSAndroid Build Coastguard Worker 				    ((int)ColorMap->Colors[i].Blue) * 11;
137*324bb76bSAndroid Build Coastguard Worker 				if (j < ColorIntens) {
138*324bb76bSAndroid Build Coastguard Worker 					ColorIntens = j;
139*324bb76bSAndroid Build Coastguard Worker 					DarkestColor = i;
140*324bb76bSAndroid Build Coastguard Worker 				}
141*324bb76bSAndroid Build Coastguard Worker 			}
142*324bb76bSAndroid Build Coastguard Worker 
143*324bb76bSAndroid Build Coastguard Worker 			/* Load the image, and dump it. */
144*324bb76bSAndroid Build Coastguard Worker 			for (i = 0; i < Height; i++) {
145*324bb76bSAndroid Build Coastguard Worker 				GifQprintf("\b\b\b\b%-4d", i);
146*324bb76bSAndroid Build Coastguard Worker 				if (DGifGetLine(GifFileIn, LineBuffer, Width) ==
147*324bb76bSAndroid Build Coastguard Worker 				    GIF_ERROR) {
148*324bb76bSAndroid Build Coastguard Worker 					break;
149*324bb76bSAndroid Build Coastguard Worker 				}
150*324bb76bSAndroid Build Coastguard Worker 				if (EGifPutLine(GifFileOut, LineBuffer,
151*324bb76bSAndroid Build Coastguard Worker 				                Width) == GIF_ERROR) {
152*324bb76bSAndroid Build Coastguard Worker 					QuitGifError(GifFileIn, GifFileOut);
153*324bb76bSAndroid Build Coastguard Worker 				}
154*324bb76bSAndroid Build Coastguard Worker 			}
155*324bb76bSAndroid Build Coastguard Worker 
156*324bb76bSAndroid Build Coastguard Worker 			if (i < Height) {
157*324bb76bSAndroid Build Coastguard Worker 				fprintf(stderr, "\nFollowing error occurred "
158*324bb76bSAndroid Build Coastguard Worker 				                "(and ignored):");
159*324bb76bSAndroid Build Coastguard Worker 				PrintGifError(GifFileIn->Error);
160*324bb76bSAndroid Build Coastguard Worker 
161*324bb76bSAndroid Build Coastguard Worker 				/* Fill in with the darkest color in color map.
162*324bb76bSAndroid Build Coastguard Worker 				 */
163*324bb76bSAndroid Build Coastguard Worker 				for (j = 0; j < Width; j++) {
164*324bb76bSAndroid Build Coastguard Worker 					LineBuffer[j] = DarkestColor;
165*324bb76bSAndroid Build Coastguard Worker 				}
166*324bb76bSAndroid Build Coastguard Worker 				for (; i < Height; i++) {
167*324bb76bSAndroid Build Coastguard Worker 					if (EGifPutLine(GifFileOut, LineBuffer,
168*324bb76bSAndroid Build Coastguard Worker 					                Width) == GIF_ERROR) {
169*324bb76bSAndroid Build Coastguard Worker 						QuitGifError(GifFileIn,
170*324bb76bSAndroid Build Coastguard Worker 						             GifFileOut);
171*324bb76bSAndroid Build Coastguard Worker 					}
172*324bb76bSAndroid Build Coastguard Worker 				}
173*324bb76bSAndroid Build Coastguard Worker 			}
174*324bb76bSAndroid Build Coastguard Worker 			break;
175*324bb76bSAndroid Build Coastguard Worker 		case EXTENSION_RECORD_TYPE:
176*324bb76bSAndroid Build Coastguard Worker 			/* pass through extension records */
177*324bb76bSAndroid Build Coastguard Worker 			if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) ==
178*324bb76bSAndroid Build Coastguard Worker 			    GIF_ERROR) {
179*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFileIn, GifFileOut);
180*324bb76bSAndroid Build Coastguard Worker 			}
181*324bb76bSAndroid Build Coastguard Worker 			if (EGifPutExtensionLeader(GifFileOut, ExtCode) ==
182*324bb76bSAndroid Build Coastguard Worker 			    GIF_ERROR) {
183*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFileIn, GifFileOut);
184*324bb76bSAndroid Build Coastguard Worker 			}
185*324bb76bSAndroid Build Coastguard Worker 			if (Extension != NULL) {
186*324bb76bSAndroid Build Coastguard Worker 				if (EGifPutExtensionBlock(
187*324bb76bSAndroid Build Coastguard Worker 				        GifFileOut, Extension[0],
188*324bb76bSAndroid Build Coastguard Worker 				        Extension + 1) == GIF_ERROR) {
189*324bb76bSAndroid Build Coastguard Worker 					QuitGifError(GifFileIn, GifFileOut);
190*324bb76bSAndroid Build Coastguard Worker 				}
191*324bb76bSAndroid Build Coastguard Worker 			}
192*324bb76bSAndroid Build Coastguard Worker 			while (Extension != NULL) {
193*324bb76bSAndroid Build Coastguard Worker 				if (DGifGetExtensionNext(
194*324bb76bSAndroid Build Coastguard Worker 				        GifFileIn, &Extension) == GIF_ERROR) {
195*324bb76bSAndroid Build Coastguard Worker 					QuitGifError(GifFileIn, GifFileOut);
196*324bb76bSAndroid Build Coastguard Worker 				}
197*324bb76bSAndroid Build Coastguard Worker 				if (Extension != NULL) {
198*324bb76bSAndroid Build Coastguard Worker 					if (EGifPutExtensionBlock(
199*324bb76bSAndroid Build Coastguard Worker 					        GifFileOut, Extension[0],
200*324bb76bSAndroid Build Coastguard Worker 					        Extension + 1) == GIF_ERROR) {
201*324bb76bSAndroid Build Coastguard Worker 						QuitGifError(GifFileIn,
202*324bb76bSAndroid Build Coastguard Worker 						             GifFileOut);
203*324bb76bSAndroid Build Coastguard Worker 					}
204*324bb76bSAndroid Build Coastguard Worker 				}
205*324bb76bSAndroid Build Coastguard Worker 			}
206*324bb76bSAndroid Build Coastguard Worker 			if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) {
207*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFileIn, GifFileOut);
208*324bb76bSAndroid Build Coastguard Worker 			}
209*324bb76bSAndroid Build Coastguard Worker 			break;
210*324bb76bSAndroid Build Coastguard Worker 		case TERMINATE_RECORD_TYPE:
211*324bb76bSAndroid Build Coastguard Worker 			break;
212*324bb76bSAndroid Build Coastguard Worker 		default: /* Should be trapped by DGifGetRecordType. */
213*324bb76bSAndroid Build Coastguard Worker 			break;
214*324bb76bSAndroid Build Coastguard Worker 		}
215*324bb76bSAndroid Build Coastguard Worker 	} while (RecordType != TERMINATE_RECORD_TYPE);
216*324bb76bSAndroid Build Coastguard Worker 
217*324bb76bSAndroid Build Coastguard Worker 	if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) {
218*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(ErrorCode);
219*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
220*324bb76bSAndroid Build Coastguard Worker 	}
221*324bb76bSAndroid Build Coastguard Worker 	if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) {
222*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(ErrorCode);
223*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
224*324bb76bSAndroid Build Coastguard Worker 	}
225*324bb76bSAndroid Build Coastguard Worker 	return 0;
226*324bb76bSAndroid Build Coastguard Worker }
227*324bb76bSAndroid Build Coastguard Worker 
228*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
229*324bb76bSAndroid Build Coastguard Worker  Close both input and output file (if open), and exit.
230*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
QuitGifError(GifFileType * GifFileIn,GifFileType * GifFileOut)231*324bb76bSAndroid Build Coastguard Worker static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) {
232*324bb76bSAndroid Build Coastguard Worker 	fprintf(stderr, "\nFollowing unrecoverable error occurred:");
233*324bb76bSAndroid Build Coastguard Worker 	if (GifFileIn != NULL) {
234*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(GifFileIn->Error);
235*324bb76bSAndroid Build Coastguard Worker 		EGifCloseFile(GifFileIn, NULL);
236*324bb76bSAndroid Build Coastguard Worker 	}
237*324bb76bSAndroid Build Coastguard Worker 	if (GifFileOut != NULL) {
238*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(GifFileOut->Error);
239*324bb76bSAndroid Build Coastguard Worker 		EGifCloseFile(GifFileOut, NULL);
240*324bb76bSAndroid Build Coastguard Worker 	}
241*324bb76bSAndroid Build Coastguard Worker 	exit(EXIT_FAILURE);
242*324bb76bSAndroid Build Coastguard Worker }
243*324bb76bSAndroid Build Coastguard Worker 
244*324bb76bSAndroid Build Coastguard Worker /* end */
245