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