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