xref: /aosp_15_r20/external/giflib/gifhisto.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1 /*****************************************************************************
2 
3 gifhisto - make a color histogram from image color frequencies
4 
5 SPDX-License-Identifier: MIT
6 
7 *****************************************************************************/
8 
9 #include <ctype.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "getarg.h"
16 #include "gif_lib.h"
17 
18 #define PROGRAM_NAME "gifhisto"
19 
20 #define DEFAULT_HISTO_WIDTH 100 /* Histogram image diemnsions. */
21 #define DEFAULT_HISTO_HEIGHT 256
22 #define HISTO_BITS_PER_PIXEL 2 /* Size of bitmap for histogram GIF. */
23 
24 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
25     "	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
26     "(C) Copyright 1989 Gershon Elber.\n";
27 static char *CtrlStr = PROGRAM_NAME
28     " v%- t%- s%-Width|Height!d!d n%-ImageNumber!d b%- h%- GifFile!*s";
29 
30 static int ImageWidth = DEFAULT_HISTO_WIDTH, ImageHeight = DEFAULT_HISTO_HEIGHT,
31            ImageN = 1;
32 static GifColorType HistoColorMap[] = {/* Constant bit map for histograms: */
33                                        {0, 0, 0},
34                                        {255, 0, 0},
35                                        {0, 255, 0},
36                                        {0, 0, 255}};
37 
38 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut);
39 
40 /******************************************************************************
41  Interpret the command line and scan the given GIF file.
42 ******************************************************************************/
main(int argc,char ** argv)43 int main(int argc, char **argv) {
44 	int i, j, ErrorCode, NumFiles, ExtCode, CodeSize, NumColors = 2,
45 	                                                  ImageNum = 0;
46 	bool Error, TextFlag = false, SizeFlag = false, ImageNFlag = false,
47 	            BackGroundFlag = false, HelpFlag = false, GifNoisyPrint;
48 	long Histogram[256];
49 	GifRecordType RecordType;
50 	GifByteType *Extension, *CodeBlock;
51 	char **FileName = NULL;
52 	GifRowType Line;
53 	GifFileType *GifFileIn = NULL, *GifFileOut = NULL;
54 
55 	/* Same image dimension vars for both Image & ImageN as only one allowed
56 	 */
57 	if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &TextFlag,
58 	                       &SizeFlag, &ImageWidth, &ImageHeight,
59 	                       &ImageNFlag, &ImageN, &BackGroundFlag, &HelpFlag,
60 	                       &NumFiles, &FileName)) != false ||
61 	    (NumFiles > 1 && !HelpFlag)) {
62 		if (Error) {
63 			GAPrintErrMsg(Error);
64 		} else if (NumFiles > 1) {
65 			GIF_MESSAGE("Error in command line parsing - one GIF "
66 			            "file please.");
67 		}
68 		GAPrintHowTo(CtrlStr);
69 		exit(EXIT_FAILURE);
70 	}
71 
72 	if (HelpFlag) {
73 		(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
74 		GAPrintHowTo(CtrlStr);
75 		exit(EXIT_SUCCESS);
76 	}
77 
78 	if (NumFiles == 1) {
79 		if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) ==
80 		    NULL) {
81 			PrintGifError(ErrorCode);
82 			exit(EXIT_FAILURE);
83 		}
84 	} else {
85 		/* Use stdin instead: */
86 		if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
87 			PrintGifError(ErrorCode);
88 			exit(EXIT_FAILURE);
89 		}
90 	}
91 
92 	for (i = 0; i < 256; i++) {
93 		Histogram[i] = 0; /* Reset counters. */
94 	}
95 	/* Scan the content of the GIF file and load the image(s) in: */
96 	do {
97 		if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) {
98 			QuitGifError(GifFileIn, GifFileOut);
99 		}
100 
101 		switch (RecordType) {
102 		case IMAGE_DESC_RECORD_TYPE:
103 			if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) {
104 				QuitGifError(GifFileIn, GifFileOut);
105 			}
106 
107 			if (GifFileIn->Image.ColorMap) {
108 				NumColors =
109 				    GifFileIn->Image.ColorMap->ColorCount;
110 			} else if (GifFileIn->SColorMap) {
111 				NumColors = GifFileIn->SColorMap->ColorCount;
112 			} else {
113 				GIF_EXIT("Neither Screen nor Image color map "
114 				         "exists.");
115 			}
116 
117 			if ((ImageHeight / NumColors) * NumColors !=
118 			    ImageHeight) {
119 				GIF_EXIT("Image height specified not dividable "
120 				         "by #colors.");
121 			}
122 
123 			if (++ImageNum == ImageN) {
124 				/* This is the image we should make histogram
125 				 * for:       */
126 				Line =
127 				    (GifRowType)malloc(GifFileIn->Image.Width *
128 				                       sizeof(GifPixelType));
129 				GifQprintf(
130 				    "\n%s: Image %d at (%d, %d) [%dx%d]:     ",
131 				    PROGRAM_NAME, ImageNum,
132 				    GifFileIn->Image.Left, GifFileIn->Image.Top,
133 				    GifFileIn->Image.Width,
134 				    GifFileIn->Image.Height);
135 
136 				for (i = 0; i < GifFileIn->Image.Height; i++) {
137 					if (DGifGetLine(
138 					        GifFileIn, Line,
139 					        GifFileIn->Image.Width) ==
140 					    GIF_ERROR) {
141 						QuitGifError(GifFileIn,
142 						             GifFileOut);
143 					}
144 					for (j = 0; j < GifFileIn->Image.Width;
145 					     j++) {
146 						Histogram[Line[j]]++;
147 					}
148 					GifQprintf("\b\b\b\b%-4d", i);
149 				}
150 
151 				free((char *)Line);
152 			} else {
153 				/* Skip the image: */
154 				/* Now read image itself in decoded form as we
155 				 * dont      */
156 				/* really care what is there, and this is much
157 				 * faster.   */
158 				if (DGifGetCode(GifFileIn, &CodeSize,
159 				                &CodeBlock) == GIF_ERROR) {
160 					QuitGifError(GifFileIn, GifFileOut);
161 				}
162 				while (CodeBlock != NULL) {
163 					if (DGifGetCodeNext(GifFileIn,
164 					                    &CodeBlock) ==
165 					    GIF_ERROR) {
166 						QuitGifError(GifFileIn,
167 						             GifFileOut);
168 					}
169 				}
170 			}
171 			break;
172 		case EXTENSION_RECORD_TYPE:
173 			/* Skip any extension blocks in file: */
174 			if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) ==
175 			    GIF_ERROR) {
176 				QuitGifError(GifFileIn, GifFileOut);
177 			}
178 
179 			while (Extension != NULL) {
180 				if (DGifGetExtensionNext(
181 				        GifFileIn, &Extension) == GIF_ERROR) {
182 					QuitGifError(GifFileIn, GifFileOut);
183 				}
184 			}
185 			break;
186 		case TERMINATE_RECORD_TYPE:
187 			break;
188 		default: /* Should be trapped by DGifGetRecordType. */
189 			break;
190 		}
191 	} while (RecordType != TERMINATE_RECORD_TYPE);
192 
193 	/* We requested suppression of the background count: */
194 	if (BackGroundFlag) {
195 		Histogram[GifFileIn->SBackGroundColor] = 0;
196 	}
197 
198 	if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) {
199 		PrintGifError(ErrorCode);
200 		exit(EXIT_FAILURE);
201 	}
202 
203 	/* We may required to dump out the histogram as text file: */
204 	if (TextFlag) {
205 		for (i = 0; i < NumColors; i++) {
206 			printf("%12ld  %3d\n", Histogram[i], i);
207 		}
208 	} else {
209 		int Color, Count;
210 		long Scaler;
211 		/* Open stdout for the histogram output file: */
212 		if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
213 			PrintGifError(ErrorCode);
214 			exit(EXIT_FAILURE);
215 		}
216 
217 		/* Dump out screen descriptor to fit histogram dimensions: */
218 		if (EGifPutScreenDesc(GifFileOut, ImageWidth, ImageHeight,
219 		                      HISTO_BITS_PER_PIXEL, 0,
220 		                      GifMakeMapObject(4, HistoColorMap)) ==
221 		    GIF_ERROR) {
222 			QuitGifError(GifFileIn, GifFileOut);
223 		}
224 
225 		/* Dump out image descriptor to fit histogram dimensions: */
226 		if (EGifPutImageDesc(GifFileOut, 0, 0, ImageWidth, ImageHeight,
227 		                     false, NULL) == GIF_ERROR) {
228 			QuitGifError(GifFileIn, GifFileOut);
229 		}
230 
231 		/* Prepare scan line for histogram file, and find scaler to
232 		 * scale    */
233 		/* histogram to be between 0 and ImageWidth: */
234 		Line = (GifRowType)malloc(ImageWidth * sizeof(GifPixelType));
235 		for (Scaler = 0, i = 0; i < NumColors; i++) {
236 			if (Histogram[i] > Scaler) {
237 				Scaler = Histogram[i];
238 			}
239 		}
240 		Scaler /= ImageWidth;
241 		if (Scaler == 0) {
242 			Scaler = 1; /* In case maximum is less than width. */
243 		}
244 		/* Dump out the image itself: */
245 		for (Count = ImageHeight, i = 0, Color = 1; i < NumColors;
246 		     i++) {
247 			int Size;
248 			if ((Size = Histogram[i] / Scaler) > ImageWidth) {
249 				Size = ImageWidth;
250 			}
251 			for (j = 0; j < Size; j++) {
252 				Line[j] = Color;
253 			}
254 			for (j = Size; j < ImageWidth; j++) {
255 				Line[j] = GifFileOut->SBackGroundColor;
256 			}
257 
258 			/* Move to next color: */
259 			if (++Color >= (1 << HISTO_BITS_PER_PIXEL)) {
260 				Color = 1;
261 			}
262 
263 			/* Dump this histogram entry as many times as required:
264 			 */
265 			for (j = 0; j < ImageHeight / NumColors; j++) {
266 				if (EGifPutLine(GifFileOut, Line, ImageWidth) ==
267 				    GIF_ERROR) {
268 					QuitGifError(GifFileIn, GifFileOut);
269 				}
270 				GifQprintf("\b\b\b\b%-4d", Count--);
271 			}
272 		}
273 
274 		if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) {
275 			PrintGifError(ErrorCode);
276 			exit(EXIT_FAILURE);
277 		}
278 	}
279 
280 	return 0;
281 }
282 
283 /******************************************************************************
284  Close both input and output file (if open), and exit.
285 ******************************************************************************/
QuitGifError(GifFileType * GifFileIn,GifFileType * GifFileOut)286 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) {
287 	if (GifFileIn != NULL) {
288 		PrintGifError(GifFileIn->Error);
289 		EGifCloseFile(GifFileIn, NULL);
290 	}
291 	if (GifFileOut != NULL) {
292 		PrintGifError(GifFileOut->Error);
293 		EGifCloseFile(GifFileOut, NULL);
294 	}
295 	exit(EXIT_FAILURE);
296 }
297 
298 /* end */
299