xref: /aosp_15_r20/external/giflib/gifecho.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1*324bb76bSAndroid Build Coastguard Worker /*****************************************************************************
2*324bb76bSAndroid Build Coastguard Worker 
3*324bb76bSAndroid Build Coastguard Worker gifecho - generate a GIF from ASCII text
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 "gifecho"
19*324bb76bSAndroid Build Coastguard Worker 
20*324bb76bSAndroid Build Coastguard Worker #define MAX_NUM_TEXT_LINES 100 /* Maximum number of lines in file. */
21*324bb76bSAndroid Build Coastguard Worker 
22*324bb76bSAndroid Build Coastguard Worker #define LINE_LEN 256 /* Maximum length of one text line. */
23*324bb76bSAndroid Build Coastguard Worker 
24*324bb76bSAndroid Build Coastguard Worker #define DEFAULT_FG_INDEX 1 /* Text foreground index. */
25*324bb76bSAndroid Build Coastguard Worker 
26*324bb76bSAndroid Build Coastguard Worker #define DEFAULT_COLOR_RED 255 /* Text foreground color. */
27*324bb76bSAndroid Build Coastguard Worker #define DEFAULT_COLOR_GREEN 255
28*324bb76bSAndroid Build Coastguard Worker #define DEFAULT_COLOR_BLUE 255
29*324bb76bSAndroid Build Coastguard Worker 
30*324bb76bSAndroid Build Coastguard Worker static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
31*324bb76bSAndroid Build Coastguard Worker     "	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
32*324bb76bSAndroid Build Coastguard Worker     "(C) Copyright 1989 Gershon Elber.\n";
33*324bb76bSAndroid Build Coastguard Worker static char *CtrlStr = PROGRAM_NAME
34*324bb76bSAndroid Build Coastguard Worker     " v%- s%-ClrMapSize!d f%-FGClr!d c%-R|G|B!d!d!d t%-\"Text\"!s h%-";
35*324bb76bSAndroid Build Coastguard Worker 
36*324bb76bSAndroid Build Coastguard Worker static unsigned int RedColor = DEFAULT_COLOR_RED,
37*324bb76bSAndroid Build Coastguard Worker                     GreenColor = DEFAULT_COLOR_GREEN,
38*324bb76bSAndroid Build Coastguard Worker                     BlueColor = DEFAULT_COLOR_BLUE;
39*324bb76bSAndroid Build Coastguard Worker 
40*324bb76bSAndroid Build Coastguard Worker static void QuitGifError(GifFileType *GifFile);
41*324bb76bSAndroid Build Coastguard Worker static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine,
42*324bb76bSAndroid Build Coastguard Worker                               int BufferWidth, int ForeGroundIndex);
43*324bb76bSAndroid Build Coastguard Worker 
44*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
45*324bb76bSAndroid Build Coastguard Worker  Interpret the command line and generate the given GIF file.
46*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
main(int argc,char ** argv)47*324bb76bSAndroid Build Coastguard Worker int main(int argc, char **argv) {
48*324bb76bSAndroid Build Coastguard Worker 	int i, j, l, ImageWidth, ImageHeight, NumOfLines, LogNumLevels,
49*324bb76bSAndroid Build Coastguard Worker 	    ErrorCode, NumLevels, ColorMapSize = 1,
50*324bb76bSAndroid Build Coastguard Worker 	                          ForeGroundIndex = DEFAULT_FG_INDEX;
51*324bb76bSAndroid Build Coastguard Worker 	bool Error, ClrMapSizeFlag = false, ForeGroundFlag = false,
52*324bb76bSAndroid Build Coastguard Worker 	            TextLineFlag = false, HelpFlag = false, ColorFlag = false,
53*324bb76bSAndroid Build Coastguard Worker 	            GifNoisyPrint = false;
54*324bb76bSAndroid Build Coastguard Worker 	char *TextLines[MAX_NUM_TEXT_LINES];
55*324bb76bSAndroid Build Coastguard Worker 	GifRowType RasterBuffer[GIF_FONT_HEIGHT];
56*324bb76bSAndroid Build Coastguard Worker 	ColorMapObject *ColorMap;
57*324bb76bSAndroid Build Coastguard Worker 	GifFileType *GifFile;
58*324bb76bSAndroid Build Coastguard Worker 
59*324bb76bSAndroid Build Coastguard Worker 	if ((Error =
60*324bb76bSAndroid Build Coastguard Worker 	         GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ClrMapSizeFlag,
61*324bb76bSAndroid Build Coastguard Worker 	                   &ColorMapSize, &ForeGroundFlag, &ForeGroundIndex,
62*324bb76bSAndroid Build Coastguard Worker 	                   &ColorFlag, &RedColor, &GreenColor, &BlueColor,
63*324bb76bSAndroid Build Coastguard Worker 	                   &TextLineFlag, &TextLines[0], &HelpFlag)) != false) {
64*324bb76bSAndroid Build Coastguard Worker 		GAPrintErrMsg(Error);
65*324bb76bSAndroid Build Coastguard Worker 		GAPrintHowTo(CtrlStr);
66*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
67*324bb76bSAndroid Build Coastguard Worker 	}
68*324bb76bSAndroid Build Coastguard Worker 
69*324bb76bSAndroid Build Coastguard Worker 	if (HelpFlag) {
70*324bb76bSAndroid Build Coastguard Worker 		(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
71*324bb76bSAndroid Build Coastguard Worker 		GAPrintHowTo(CtrlStr);
72*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_SUCCESS);
73*324bb76bSAndroid Build Coastguard Worker 	}
74*324bb76bSAndroid Build Coastguard Worker 
75*324bb76bSAndroid Build Coastguard Worker 	if (ForeGroundIndex > 255 || ForeGroundIndex < 1) {
76*324bb76bSAndroid Build Coastguard Worker 		GIF_EXIT(
77*324bb76bSAndroid Build Coastguard Worker 		    "Foregound (-f) should be in the range 1..255, aborted.");
78*324bb76bSAndroid Build Coastguard Worker 	}
79*324bb76bSAndroid Build Coastguard Worker 
80*324bb76bSAndroid Build Coastguard Worker 	if (ColorMapSize > 8 || ColorMapSize < 1) {
81*324bb76bSAndroid Build Coastguard Worker 		GIF_EXIT(
82*324bb76bSAndroid Build Coastguard Worker 		    "ColorMapSize (-s) should be in the range 1..8, aborted.");
83*324bb76bSAndroid Build Coastguard Worker 	}
84*324bb76bSAndroid Build Coastguard Worker 
85*324bb76bSAndroid Build Coastguard Worker 	if (TextLineFlag) {
86*324bb76bSAndroid Build Coastguard Worker 		NumOfLines = 1;
87*324bb76bSAndroid Build Coastguard Worker 		ImageHeight = GIF_FONT_HEIGHT;
88*324bb76bSAndroid Build Coastguard Worker 		ImageWidth = GIF_FONT_WIDTH * strlen(TextLines[0]);
89*324bb76bSAndroid Build Coastguard Worker 	} else {
90*324bb76bSAndroid Build Coastguard Worker 		char Line[LINE_LEN];
91*324bb76bSAndroid Build Coastguard Worker 		NumOfLines = l = 0;
92*324bb76bSAndroid Build Coastguard Worker 		while (fgets(Line, LINE_LEN - 1, stdin)) {
93*324bb76bSAndroid Build Coastguard Worker 			for (i = strlen(Line); i > 0 && Line[i - 1] <= ' ';
94*324bb76bSAndroid Build Coastguard Worker 			     i--) {
95*324bb76bSAndroid Build Coastguard Worker 				;
96*324bb76bSAndroid Build Coastguard Worker 			}
97*324bb76bSAndroid Build Coastguard Worker 			Line[i] = 0;
98*324bb76bSAndroid Build Coastguard Worker 			if (l < i) {
99*324bb76bSAndroid Build Coastguard Worker 				l = i;
100*324bb76bSAndroid Build Coastguard Worker 			}
101*324bb76bSAndroid Build Coastguard Worker 			TextLines[NumOfLines++] = strdup(Line);
102*324bb76bSAndroid Build Coastguard Worker 			if (NumOfLines == MAX_NUM_TEXT_LINES) {
103*324bb76bSAndroid Build Coastguard Worker 				GIF_EXIT(
104*324bb76bSAndroid Build Coastguard Worker 				    "Input file has too many lines, aborted.");
105*324bb76bSAndroid Build Coastguard Worker 			}
106*324bb76bSAndroid Build Coastguard Worker 		}
107*324bb76bSAndroid Build Coastguard Worker 		if (NumOfLines == 0) {
108*324bb76bSAndroid Build Coastguard Worker 			GIF_EXIT("No input text, aborted.");
109*324bb76bSAndroid Build Coastguard Worker 		}
110*324bb76bSAndroid Build Coastguard Worker 		ImageHeight = GIF_FONT_HEIGHT * NumOfLines;
111*324bb76bSAndroid Build Coastguard Worker 		ImageWidth = GIF_FONT_WIDTH * l;
112*324bb76bSAndroid Build Coastguard Worker 	}
113*324bb76bSAndroid Build Coastguard Worker 
114*324bb76bSAndroid Build Coastguard Worker 	/* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines (one text
115*324bb76bSAndroid Build Coastguard Worker 	 * line). */
116*324bb76bSAndroid Build Coastguard Worker 	for (i = 0; i < GIF_FONT_HEIGHT; i++) {
117*324bb76bSAndroid Build Coastguard Worker 		if ((RasterBuffer[i] = (GifRowType)malloc(
118*324bb76bSAndroid Build Coastguard Worker 		         sizeof(GifPixelType) * ImageWidth)) == NULL) {
119*324bb76bSAndroid Build Coastguard Worker 			GIF_EXIT(
120*324bb76bSAndroid Build Coastguard Worker 			    "Failed to allocate memory required, aborted.");
121*324bb76bSAndroid Build Coastguard Worker 		}
122*324bb76bSAndroid Build Coastguard Worker 	}
123*324bb76bSAndroid Build Coastguard Worker 
124*324bb76bSAndroid Build Coastguard Worker 	/* Open stdout for the output file: */
125*324bb76bSAndroid Build Coastguard Worker 	if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
126*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(ErrorCode);
127*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
128*324bb76bSAndroid Build Coastguard Worker 	}
129*324bb76bSAndroid Build Coastguard Worker 
130*324bb76bSAndroid Build Coastguard Worker 	/* Dump out screen description with given size and generated color map:
131*324bb76bSAndroid Build Coastguard Worker 	 */
132*324bb76bSAndroid Build Coastguard Worker 	for (LogNumLevels = 1, NumLevels = 2; NumLevels < ForeGroundIndex;
133*324bb76bSAndroid Build Coastguard Worker 	     LogNumLevels++, NumLevels <<= 1) {
134*324bb76bSAndroid Build Coastguard Worker 		;
135*324bb76bSAndroid Build Coastguard Worker 	}
136*324bb76bSAndroid Build Coastguard Worker 	if (NumLevels < (1 << ColorMapSize)) {
137*324bb76bSAndroid Build Coastguard Worker 		NumLevels = (1 << ColorMapSize);
138*324bb76bSAndroid Build Coastguard Worker 		LogNumLevels = ColorMapSize;
139*324bb76bSAndroid Build Coastguard Worker 	}
140*324bb76bSAndroid Build Coastguard Worker 
141*324bb76bSAndroid Build Coastguard Worker 	if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) {
142*324bb76bSAndroid Build Coastguard Worker 		GIF_EXIT("Failed to allocate memory required, aborted.");
143*324bb76bSAndroid Build Coastguard Worker 	}
144*324bb76bSAndroid Build Coastguard Worker 
145*324bb76bSAndroid Build Coastguard Worker 	for (i = 0; i < NumLevels; i++) {
146*324bb76bSAndroid Build Coastguard Worker 		ColorMap->Colors[i].Red = ColorMap->Colors[i].Green =
147*324bb76bSAndroid Build Coastguard Worker 		    ColorMap->Colors[i].Blue = 0;
148*324bb76bSAndroid Build Coastguard Worker 	}
149*324bb76bSAndroid Build Coastguard Worker 	ColorMap->Colors[ForeGroundIndex].Red = RedColor;
150*324bb76bSAndroid Build Coastguard Worker 	ColorMap->Colors[ForeGroundIndex].Green = GreenColor;
151*324bb76bSAndroid Build Coastguard Worker 	ColorMap->Colors[ForeGroundIndex].Blue = BlueColor;
152*324bb76bSAndroid Build Coastguard Worker 
153*324bb76bSAndroid Build Coastguard Worker 	if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0,
154*324bb76bSAndroid Build Coastguard Worker 	                      ColorMap) == GIF_ERROR) {
155*324bb76bSAndroid Build Coastguard Worker 		QuitGifError(GifFile);
156*324bb76bSAndroid Build Coastguard Worker 	}
157*324bb76bSAndroid Build Coastguard Worker 
158*324bb76bSAndroid Build Coastguard Worker 	/* Dump out the image descriptor: */
159*324bb76bSAndroid Build Coastguard Worker 	if (EGifPutImageDesc(GifFile, 0, 0, ImageWidth, ImageHeight, false,
160*324bb76bSAndroid Build Coastguard Worker 	                     NULL) == GIF_ERROR) {
161*324bb76bSAndroid Build Coastguard Worker 		QuitGifError(GifFile);
162*324bb76bSAndroid Build Coastguard Worker 	}
163*324bb76bSAndroid Build Coastguard Worker 
164*324bb76bSAndroid Build Coastguard Worker 	GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]:     ", PROGRAM_NAME,
165*324bb76bSAndroid Build Coastguard Worker 	           GifFile->Image.Left, GifFile->Image.Top,
166*324bb76bSAndroid Build Coastguard Worker 	           GifFile->Image.Width, GifFile->Image.Height);
167*324bb76bSAndroid Build Coastguard Worker 
168*324bb76bSAndroid Build Coastguard Worker 	for (i = l = 0; i < NumOfLines; i++) {
169*324bb76bSAndroid Build Coastguard Worker 		GenRasterTextLine(RasterBuffer, TextLines[i], ImageWidth,
170*324bb76bSAndroid Build Coastguard Worker 		                  ForeGroundIndex);
171*324bb76bSAndroid Build Coastguard Worker 		for (j = 0; j < GIF_FONT_HEIGHT; j++) {
172*324bb76bSAndroid Build Coastguard Worker 			if (EGifPutLine(GifFile, RasterBuffer[j], ImageWidth) ==
173*324bb76bSAndroid Build Coastguard Worker 			    GIF_ERROR) {
174*324bb76bSAndroid Build Coastguard Worker 				QuitGifError(GifFile);
175*324bb76bSAndroid Build Coastguard Worker 			}
176*324bb76bSAndroid Build Coastguard Worker 			GifQprintf("\b\b\b\b%-4d", l++);
177*324bb76bSAndroid Build Coastguard Worker 		}
178*324bb76bSAndroid Build Coastguard Worker 	}
179*324bb76bSAndroid Build Coastguard Worker 
180*324bb76bSAndroid Build Coastguard Worker 	if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) {
181*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(ErrorCode);
182*324bb76bSAndroid Build Coastguard Worker 		exit(EXIT_FAILURE);
183*324bb76bSAndroid Build Coastguard Worker 	}
184*324bb76bSAndroid Build Coastguard Worker 
185*324bb76bSAndroid Build Coastguard Worker 	return 0;
186*324bb76bSAndroid Build Coastguard Worker }
187*324bb76bSAndroid Build Coastguard Worker 
188*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
189*324bb76bSAndroid Build Coastguard Worker  Generate raster bits corresponding to given text
190*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
GenRasterTextLine(GifRowType * RasterBuffer,char * TextLine,int BufferWidth,int ForeGroundIndex)191*324bb76bSAndroid Build Coastguard Worker static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine,
192*324bb76bSAndroid Build Coastguard Worker                               int BufferWidth, int ForeGroundIndex) {
193*324bb76bSAndroid Build Coastguard Worker 	unsigned char Byte, Mask;
194*324bb76bSAndroid Build Coastguard Worker 	int i, j, k, CharPosX, Len = strlen(TextLine);
195*324bb76bSAndroid Build Coastguard Worker 
196*324bb76bSAndroid Build Coastguard Worker 	for (i = 0; i < BufferWidth; i++) {
197*324bb76bSAndroid Build Coastguard Worker 		for (j = 0; j < GIF_FONT_HEIGHT; j++) {
198*324bb76bSAndroid Build Coastguard Worker 			RasterBuffer[j][i] = 0;
199*324bb76bSAndroid Build Coastguard Worker 		}
200*324bb76bSAndroid Build Coastguard Worker 	}
201*324bb76bSAndroid Build Coastguard Worker 
202*324bb76bSAndroid Build Coastguard Worker 	for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) {
203*324bb76bSAndroid Build Coastguard Worker 		unsigned char c = TextLine[i];
204*324bb76bSAndroid Build Coastguard Worker 		for (j = 0; j < GIF_FONT_HEIGHT; j++) {
205*324bb76bSAndroid Build Coastguard Worker 			Byte = GifAsciiTable8x8[(unsigned short)c][j];
206*324bb76bSAndroid Build Coastguard Worker 			for (k = 0, Mask = 128; k < GIF_FONT_WIDTH;
207*324bb76bSAndroid Build Coastguard Worker 			     k++, Mask >>= 1) {
208*324bb76bSAndroid Build Coastguard Worker 				if (Byte & Mask) {
209*324bb76bSAndroid Build Coastguard Worker 					RasterBuffer[j][CharPosX + k] =
210*324bb76bSAndroid Build Coastguard Worker 					    ForeGroundIndex;
211*324bb76bSAndroid Build Coastguard Worker 				}
212*324bb76bSAndroid Build Coastguard Worker 			}
213*324bb76bSAndroid Build Coastguard Worker 		}
214*324bb76bSAndroid Build Coastguard Worker 	}
215*324bb76bSAndroid Build Coastguard Worker }
216*324bb76bSAndroid Build Coastguard Worker 
217*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
218*324bb76bSAndroid Build Coastguard Worker  * Close output file (if open), and exit.
219*324bb76bSAndroid Build Coastguard Worker  ******************************************************************************/
QuitGifError(GifFileType * GifFile)220*324bb76bSAndroid Build Coastguard Worker static void QuitGifError(GifFileType *GifFile) {
221*324bb76bSAndroid Build Coastguard Worker 	if (GifFile != NULL) {
222*324bb76bSAndroid Build Coastguard Worker 		PrintGifError(GifFile->Error);
223*324bb76bSAndroid Build Coastguard Worker 		EGifCloseFile(GifFile, NULL);
224*324bb76bSAndroid Build Coastguard Worker 	}
225*324bb76bSAndroid Build Coastguard Worker 	exit(EXIT_FAILURE);
226*324bb76bSAndroid Build Coastguard Worker }
227*324bb76bSAndroid Build Coastguard Worker 
228*324bb76bSAndroid Build Coastguard Worker /* end */
229