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