xref: /aosp_15_r20/external/giflib/giftext.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1 /*****************************************************************************
2 
3 giftext - dump GIF pixels and metadata as text
4 
5 SPDX-License-Identifier: MIT
6 
7 *****************************************************************************/
8 
9 #include <ctype.h>
10 #include <fcntl.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #ifdef _WIN32
16 #include <io.h>
17 #endif /* _WIN32 */
18 
19 #include "getarg.h"
20 #include "gif_lib.h"
21 
22 #define PROGRAM_NAME "giftext"
23 
24 #define MAKE_PRINTABLE(c) (isprint(c) ? (c) : ' ')
25 
26 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
27     "	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
28     "(C) Copyright 1989 Gershon Elber.\n";
29 static char *CtrlStr = PROGRAM_NAME " v%- c%- e%- z%- p%- r%- h%- GifFile!*s";
30 
31 static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock,
32                            bool Reset);
33 static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset);
34 static void PrintExtBlock(GifByteType *Extension, bool Reset);
35 static void PrintLZCodes(GifFileType *GifFile);
36 
37 /******************************************************************************
38  Interpret the command line and scan the given GIF file.
39 ******************************************************************************/
main(int argc,char ** argv)40 int main(int argc, char **argv) {
41 	int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1;
42 	bool Error, ColorMapFlag = false, EncodedFlag = false,
43 	            LZCodesFlag = false, PixelFlag = false, HelpFlag = false,
44 	            RawFlag = false, GifNoisyPrint;
45 	char *GifFileName, **FileName = NULL;
46 	GifPixelType *Line;
47 	GifRecordType RecordType;
48 	GifByteType *CodeBlock, *Extension;
49 	GifFileType *GifFile;
50 
51 	if ((Error =
52 	         GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ColorMapFlag,
53 	                   &EncodedFlag, &LZCodesFlag, &PixelFlag, &RawFlag,
54 	                   &HelpFlag, &NumFiles, &FileName)) != false ||
55 	    (NumFiles > 1 && !HelpFlag)) {
56 		if (Error) {
57 			GAPrintErrMsg(Error);
58 		} else if (NumFiles > 1) {
59 			GIF_MESSAGE("Error in command line parsing - one GIF "
60 			            "file please.");
61 		}
62 		GAPrintHowTo(CtrlStr);
63 		exit(EXIT_FAILURE);
64 	}
65 
66 	if (HelpFlag) {
67 		(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
68 		GAPrintHowTo(CtrlStr);
69 		exit(EXIT_SUCCESS);
70 	}
71 
72 	if (NumFiles == 1) {
73 		GifFileName = *FileName;
74 		if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) ==
75 		    NULL) {
76 			PrintGifError(ErrorCode);
77 			exit(EXIT_FAILURE);
78 		}
79 	} else {
80 		/* Use stdin instead: */
81 		GifFileName = "Stdin";
82 		if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
83 			PrintGifError(ErrorCode);
84 			exit(EXIT_FAILURE);
85 		}
86 	}
87 
88 	/* Because we write binary data - make sure no text will be written. */
89 	if (RawFlag) {
90 		ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false;
91 #ifdef _WIN32
92 		_setmode(1, O_BINARY); /* Make sure it is in binary mode. */
93 #endif                                 /* _WIN32 */
94 	} else {
95 		printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n",
96 		       GifFileName, GifFile->SWidth, GifFile->SHeight);
97 		printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround "
98 		       "= %d, Aspect = %d.\n",
99 		       GifFile->SColorResolution,
100 		       GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel
101 		                          : 0,
102 		       GifFile->SBackGroundColor, GifFile->AspectByte);
103 		if (GifFile->SColorMap) {
104 			printf("\tHas Global Color Map.\n\n");
105 		} else {
106 			printf("\tNo Global Color Map.\n\n");
107 		}
108 		if (ColorMapFlag && GifFile->SColorMap) {
109 			printf("\tGlobal Color Map:\n");
110 			Len = GifFile->SColorMap->ColorCount;
111 			printf("\tSort Flag: %s\n",
112 			       GifFile->SColorMap->SortFlag ? "on" : "off");
113 			for (i = 0; i < Len; i += 4) {
114 				for (j = 0; j < 4 && j < Len; j++) {
115 					printf("%3d: %02xh %02xh %02xh   ",
116 					       i + j,
117 					       GifFile->SColorMap->Colors[i + j]
118 					           .Red,
119 					       GifFile->SColorMap->Colors[i + j]
120 					           .Green,
121 					       GifFile->SColorMap->Colors[i + j]
122 					           .Blue);
123 				}
124 				printf("\n");
125 			}
126 		}
127 	}
128 
129 	do {
130 		if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
131 			PrintGifError(GifFile->Error);
132 			exit(EXIT_FAILURE);
133 		}
134 		switch (RecordType) {
135 		case IMAGE_DESC_RECORD_TYPE:
136 			if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
137 				PrintGifError(GifFile->Error);
138 				exit(EXIT_FAILURE);
139 			}
140 			if (!RawFlag) {
141 				printf(
142 				    "\nImage #%d:\n\n\tImage Size - Left = %d, "
143 				    "Top = %d, Width = %d, Height = %d.\n",
144 				    ImageNum++, GifFile->Image.Left,
145 				    GifFile->Image.Top, GifFile->Image.Width,
146 				    GifFile->Image.Height);
147 				printf("\tImage is %s", GifFile->Image.Interlace
148 				                            ? "Interlaced"
149 				                            : "Non Interlaced");
150 				if (GifFile->Image.ColorMap != NULL) {
151 					printf(", BitsPerPixel = %d.\n",
152 					       GifFile->Image.ColorMap
153 					           ->BitsPerPixel);
154 				} else {
155 					printf(".\n");
156 				}
157 				if (GifFile->Image.ColorMap) {
158 					printf("\tImage Has Color Map.\n");
159 				} else {
160 					printf("\tNo Image Color Map.\n");
161 				}
162 				if (ColorMapFlag && GifFile->Image.ColorMap) {
163 					printf("\tSort Flag: %s\n",
164 					       GifFile->Image.ColorMap->SortFlag
165 					           ? "on"
166 					           : "off");
167 					Len = 1 << GifFile->Image.ColorMap
168 					               ->BitsPerPixel;
169 					for (i = 0; i < Len; i += 4) {
170 						for (j = 0; j < 4 && j < Len;
171 						     j++) {
172 							printf(
173 							    "%3d: %02xh %02xh "
174 							    "%02xh   ",
175 							    i + j,
176 							    GifFile->Image
177 							        .ColorMap
178 							        ->Colors[i + j]
179 							        .Red,
180 							    GifFile->Image
181 							        .ColorMap
182 							        ->Colors[i + j]
183 							        .Green,
184 							    GifFile->Image
185 							        .ColorMap
186 							        ->Colors[i + j]
187 							        .Blue);
188 						}
189 						printf("\n");
190 					}
191 				}
192 			}
193 
194 			if (EncodedFlag) {
195 				if (DGifGetCode(GifFile, &CodeSize,
196 				                &CodeBlock) == GIF_ERROR) {
197 					PrintGifError(GifFile->Error);
198 					exit(EXIT_FAILURE);
199 				}
200 				printf("\nImage LZ compressed Codes (Code Size "
201 				       "= %d):\n",
202 				       CodeSize);
203 				PrintCodeBlock(GifFile, CodeBlock, true);
204 				while (CodeBlock != NULL) {
205 					if (DGifGetCodeNext(GifFile,
206 					                    &CodeBlock) ==
207 					    GIF_ERROR) {
208 						PrintGifError(GifFile->Error);
209 						exit(EXIT_FAILURE);
210 					}
211 					PrintCodeBlock(GifFile, CodeBlock,
212 					               false);
213 				}
214 			} else if (LZCodesFlag) {
215 				PrintLZCodes(GifFile);
216 			} else if (PixelFlag) {
217 				Line = (GifPixelType *)malloc(
218 				    GifFile->Image.Width *
219 				    sizeof(GifPixelType));
220 				for (i = 0; i < GifFile->Image.Height; i++) {
221 					if (DGifGetLine(GifFile, Line,
222 					                GifFile->Image.Width) ==
223 					    GIF_ERROR) {
224 						PrintGifError(GifFile->Error);
225 						exit(EXIT_FAILURE);
226 					}
227 					PrintPixelBlock(
228 					    Line, GifFile->Image.Width, i == 0);
229 				}
230 				PrintPixelBlock(NULL, GifFile->Image.Width,
231 				                false);
232 				free((char *)Line);
233 			} else if (RawFlag) {
234 				Line = (GifPixelType *)malloc(
235 				    GifFile->Image.Width *
236 				    sizeof(GifPixelType));
237 				for (i = 0; i < GifFile->Image.Height; i++) {
238 					if (DGifGetLine(GifFile, Line,
239 					                GifFile->Image.Width) ==
240 					    GIF_ERROR) {
241 						PrintGifError(GifFile->Error);
242 						exit(EXIT_FAILURE);
243 					}
244 					fwrite(Line, 1, GifFile->Image.Width,
245 					       stdout);
246 				}
247 				free((char *)Line);
248 			} else {
249 				/* Skip the image: */
250 				if (DGifGetCode(GifFile, &CodeSize,
251 				                &CodeBlock) == GIF_ERROR) {
252 					PrintGifError(GifFile->Error);
253 					exit(EXIT_FAILURE);
254 				}
255 				while (CodeBlock != NULL) {
256 					if (DGifGetCodeNext(GifFile,
257 					                    &CodeBlock) ==
258 					    GIF_ERROR) {
259 						PrintGifError(GifFile->Error);
260 						exit(EXIT_FAILURE);
261 					}
262 				}
263 			}
264 			break;
265 		case EXTENSION_RECORD_TYPE:
266 			if (DGifGetExtension(GifFile, &ExtCode, &Extension) ==
267 			    GIF_ERROR) {
268 				PrintGifError(GifFile->Error);
269 				exit(EXIT_FAILURE);
270 			}
271 			if (!RawFlag) {
272 				putchar('\n');
273 				switch (ExtCode) {
274 				case COMMENT_EXT_FUNC_CODE:
275 					printf("GIF89 comment");
276 					break;
277 				case GRAPHICS_EXT_FUNC_CODE:
278 					printf("GIF89 graphics control");
279 					break;
280 				case PLAINTEXT_EXT_FUNC_CODE:
281 					printf("GIF89 plaintext");
282 					break;
283 				case APPLICATION_EXT_FUNC_CODE:
284 					printf("GIF89 application block");
285 					break;
286 				default:
287 					printf(
288 					    "Extension record of unknown type");
289 					break;
290 				}
291 				printf(" (Ext Code = %d [%c]):\n", ExtCode,
292 				       MAKE_PRINTABLE(ExtCode));
293 				PrintExtBlock(Extension, true);
294 
295 				if (ExtCode == GRAPHICS_EXT_FUNC_CODE) {
296 					GraphicsControlBlock gcb;
297 					if (Extension == NULL) {
298 						printf("Invalid extension "
299 						       "block\n");
300 						GifFile->Error =
301 						    D_GIF_ERR_IMAGE_DEFECT;
302 						PrintGifError(GifFile->Error);
303 						exit(EXIT_FAILURE);
304 					}
305 					if (DGifExtensionToGCB(
306 					        Extension[0], Extension + 1,
307 					        &gcb) == GIF_ERROR) {
308 						PrintGifError(GifFile->Error);
309 						exit(EXIT_FAILURE);
310 					}
311 					printf("\tDisposal Mode: %d\n",
312 					       gcb.DisposalMode);
313 					printf("\tUser Input Flag: %d\n",
314 					       gcb.UserInputFlag);
315 					printf("\tTransparency on: %s\n",
316 					       gcb.TransparentColor != -1
317 					           ? "yes"
318 					           : "no");
319 					printf("\tDelayTime: %d\n",
320 					       gcb.DelayTime);
321 					printf("\tTransparent Index: %d\n",
322 					       gcb.TransparentColor);
323 				}
324 			}
325 			for (;;) {
326 				if (DGifGetExtensionNext(GifFile, &Extension) ==
327 				    GIF_ERROR) {
328 					PrintGifError(GifFile->Error);
329 					exit(EXIT_FAILURE);
330 				}
331 				if (Extension == NULL) {
332 					break;
333 				}
334 				PrintExtBlock(Extension, false);
335 			}
336 			break;
337 		case TERMINATE_RECORD_TYPE:
338 			break;
339 		default: /* Should be trapped by DGifGetRecordType */
340 			break;
341 		}
342 	} while (RecordType != TERMINATE_RECORD_TYPE);
343 
344 	if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) {
345 		PrintGifError(ErrorCode);
346 		exit(EXIT_FAILURE);
347 	}
348 
349 	if (!RawFlag) {
350 		printf("\nGIF file terminated normally.\n");
351 	}
352 
353 	return 0;
354 }
355 
356 /******************************************************************************
357  Print the given CodeBlock - a string in pascal notation (size in first
358  place). Save local information so printing can be performed continuously,
359  or reset to start state if Reset. If CodeBlock is NULL, output is flushed
360 ******************************************************************************/
PrintCodeBlock(GifFileType * GifFile,GifByteType * CodeBlock,bool Reset)361 static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock,
362                            bool Reset) {
363 	static int CrntPlace = 0;
364 	static long CodeCount = 0;
365 	int i, Len;
366 
367 	if (Reset || CodeBlock == NULL) {
368 		if (CodeBlock == NULL) {
369 			long NumBytes = 0;
370 			if (CrntPlace > 0) {
371 				printf("\n");
372 				CodeCount += CrntPlace - 16;
373 			}
374 			if (GifFile->Image.ColorMap) {
375 				NumBytes =
376 				    ((((long)GifFile->Image.Width) *
377 				      GifFile->Image.Height) *
378 				     GifFile->Image.ColorMap->BitsPerPixel) /
379 				    8;
380 			} else if (GifFile->SColorMap != NULL) {
381 				NumBytes = ((((long)GifFile->Image.Width) *
382 				             GifFile->Image.Height) *
383 				            GifFile->SColorMap->BitsPerPixel) /
384 				           8;
385 			}
386 			/* FIXME: What should the compression ratio be if no
387 			 * color table? */
388 			if (NumBytes > 0) {
389 				int Percent = 100 * CodeCount / NumBytes;
390 				printf("\nCompression ratio: %ld/%ld (%d%%).\n",
391 				       CodeCount, NumBytes, Percent);
392 			}
393 			return;
394 		}
395 		CrntPlace = 0;
396 		CodeCount = 0;
397 	}
398 
399 	// cppcheck-suppress nullPointerRedundantCheck
400 	Len = CodeBlock[0];
401 	for (i = 1; i <= Len; i++) {
402 		if (CrntPlace == 0) {
403 			printf("\n%05lxh:  ", CodeCount);
404 			CodeCount += 16;
405 		}
406 		(void)printf(" %02xh", CodeBlock[i]);
407 		if (++CrntPlace >= 16) {
408 			CrntPlace = 0;
409 		}
410 	}
411 }
412 
413 /******************************************************************************
414  Print the given Extension - a string in pascal notation (size in first
415  place). Save local information so printing can be performed continuously,
416  or reset to start state if Reset. If Extension is NULL, output is flushed
417 ******************************************************************************/
PrintExtBlock(GifByteType * Extension,bool Reset)418 static void PrintExtBlock(GifByteType *Extension, bool Reset) {
419 	static int CrntPlace = 0;
420 	static long ExtCount = 0;
421 	static char HexForm[49], AsciiForm[17];
422 
423 	if (Reset || Extension == NULL) {
424 		if (Extension == NULL) {
425 			if (CrntPlace > 0) {
426 				HexForm[CrntPlace * 3] = 0;
427 				AsciiForm[CrntPlace] = 0;
428 				printf("\n%05lx: %-49s  %-17s\n", ExtCount,
429 				       HexForm, AsciiForm);
430 				return;
431 			} else {
432 				printf("\n");
433 			}
434 		}
435 		CrntPlace = 0;
436 		ExtCount = 0;
437 	}
438 
439 	if (Extension != NULL) {
440 		int i, Len;
441 		Len = Extension[0];
442 		for (i = 1; i <= Len; i++) {
443 			(void)snprintf(&HexForm[CrntPlace * 3], 3, " %02x",
444 			               Extension[i]);
445 			(void)snprintf(&AsciiForm[CrntPlace], 3, "%c",
446 			               MAKE_PRINTABLE(Extension[i]));
447 			if (++CrntPlace == 16) {
448 				HexForm[CrntPlace * 3] = 0;
449 				AsciiForm[CrntPlace] = 0;
450 				printf("\n%05lx: %-49s  %-17s", ExtCount,
451 				       HexForm, AsciiForm);
452 				ExtCount += 16;
453 				CrntPlace = 0;
454 			}
455 		}
456 	}
457 }
458 
459 /******************************************************************************
460  Print the given PixelBlock of length Len.
461  Save local information so printing can be performed continuously,
462  or reset to start state if Reset. If PixelBlock is NULL, output is flushed
463 ******************************************************************************/
PrintPixelBlock(GifByteType * PixelBlock,int Len,bool Reset)464 static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset) {
465 	static int CrntPlace = 0;
466 	static long ExtCount = 0;
467 	static char HexForm[49], AsciiForm[17];
468 	int i;
469 
470 	if (Reset || PixelBlock == NULL) {
471 		if (PixelBlock == NULL) {
472 			if (CrntPlace > 0) {
473 				HexForm[CrntPlace * 3] = 0;
474 				AsciiForm[CrntPlace] = 0;
475 				printf("\n%05lx: %-49s  %-17s\n", ExtCount,
476 				       HexForm, AsciiForm);
477 			} else {
478 				printf("\n");
479 			}
480 		}
481 		CrntPlace = 0;
482 		ExtCount = 0;
483 		if (PixelBlock == NULL) {
484 			return;
485 		}
486 	}
487 
488 	for (i = 0; i < Len; i++) {
489 		(void)snprintf(&HexForm[CrntPlace * 3], 3, " %02x",
490 		               PixelBlock[i]);
491 		(void)snprintf(&AsciiForm[CrntPlace], 3, "%c",
492 		               MAKE_PRINTABLE(PixelBlock[i]));
493 		if (++CrntPlace == 16) {
494 			HexForm[CrntPlace * 3] = 0;
495 			AsciiForm[CrntPlace] = 0;
496 			printf("\n%05lx: %-49s  %-17s", ExtCount, HexForm,
497 			       AsciiForm);
498 			ExtCount += 16;
499 			CrntPlace = 0;
500 		}
501 	}
502 }
503 
504 /******************************************************************************
505  Print the image as LZ codes (each 12bits), until EOF marker is reached.
506 ******************************************************************************/
PrintLZCodes(GifFileType * GifFile)507 static void PrintLZCodes(GifFileType *GifFile) {
508 	int Code, CrntPlace = 0;
509 	long CodeCount = 0;
510 
511 	do {
512 		if (CrntPlace == 0) {
513 			printf("\n%05lx:", CodeCount);
514 		}
515 		if (DGifGetLZCodes(GifFile, &Code) == GIF_ERROR) {
516 			PrintGifError(GifFile->Error);
517 			exit(EXIT_FAILURE);
518 		}
519 		if (Code >= 0) {
520 			printf(" %03x", Code); /* EOF Code is returned as -1. */
521 		}
522 		CodeCount++;
523 		if (++CrntPlace >= 16) {
524 			CrntPlace = 0;
525 		}
526 	} while (Code >= 0);
527 }
528 
529 /* end */
530