1 /*****************************************************************************
2 
3 gifbg - generate a test-pattern GIF
4 
5 SPDX-License-Identifier: MIT
6 
7 *****************************************************************************/
8 
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <string.h>
12 #include <stdbool.h>
13 #include <stdlib.h>
14 
15 #include "gif_lib.h"
16 #include "getarg.h"
17 
18 #define PROGRAM_NAME	"gifbg"
19 
20 #define DEFAULT_WIDTH	640
21 #define DEFAULT_HEIGHT	350
22 
23 #define DEFAULT_COLOR_RED	0
24 #define DEFAULT_COLOR_GREEN	0
25 #define DEFAULT_COLOR_BLUE	255
26 
27 #define DEFAULT_MIN_INTENSITY	10			      /* In percent. */
28 #define DEFAULT_MAX_INTENSITY	100
29 
30 #define DEFAULT_NUM_LEVELS	16     /* Number of colors to gen in image. */
31 
32 #define DIR_NONE	0	     /* Direction the levels can be changed: */
33 #define DIR_TOP		1
34 #define DIR_TOP_RIGHT	2
35 #define DIR_RIGHT	3
36 #define DIR_BOT_RIGHT	4
37 #define DIR_BOT		5
38 #define DIR_BOT_LEFT	6
39 #define DIR_LEFT	7
40 #define DIR_TOP_LEFT	8
41 
42 #define DEFAULT_DIR	"T"			   /* TOP (North) direction. */
43 
44 static char
45     *VersionStr =
46 	PROGRAM_NAME
47 	VERSION_COOKIE
48 	"	Gershon Elber,	"
49 	__DATE__ ",   " __TIME__ "\n"
50 	"(C) Copyright 1989 Gershon Elber.\n";
51 static char
52     *CtrlStr =
53 	PROGRAM_NAME
54 	" v%- d%-Dir!s l%-#Lvls!d c%-R|G|B!d!d!d m%-MinI!d M%-MaxI!d s%-W|H!d!d h%-";
55 
56 static int
57     MaximumIntensity = DEFAULT_MAX_INTENSITY,		      /* In percent. */
58     MinimumIntensity = DEFAULT_MIN_INTENSITY,
59     NumLevels = DEFAULT_NUM_LEVELS,
60     ImageWidth = DEFAULT_WIDTH,
61     ImageHeight = DEFAULT_HEIGHT,
62     Direction;
63 static unsigned int
64     RedColor = DEFAULT_COLOR_RED,
65     GreenColor = DEFAULT_COLOR_GREEN,
66     BlueColor = DEFAULT_COLOR_BLUE;
67 
68 static void QuitGifError(GifFileType *GifFile);
69 
70 /******************************************************************************
71  Interpret the command line and scan the given GIF file.
72 ******************************************************************************/
main(int argc,char ** argv)73 int main(int argc, char **argv)
74 {
75     int	i, l, LevelWidth, LogNumLevels, ErrorCode, Count = 0;
76     bool Error, FlipDir, DoAllMaximum = false,
77 	DirectionFlag = false, LevelsFlag = false, ColorFlag = false,
78 	MinFlag = false, MaxFlag = false, SizeFlag = false, HelpFlag = false;
79     GifPixelType Color;
80     char *DirectionStr = DEFAULT_DIR;
81     GifRowType Line;
82     ColorMapObject *ColorMap;
83     GifFileType *GifFile;
84 
85     if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint,
86 		&DirectionFlag, &DirectionStr, &LevelsFlag, &NumLevels,
87 		&ColorFlag, &RedColor, &GreenColor, &BlueColor,
88 		&MinFlag, &MinimumIntensity, &MaxFlag, &MaximumIntensity,
89 		&SizeFlag, &ImageWidth, &ImageHeight,
90 		&HelpFlag)) != false) {
91 	GAPrintErrMsg(Error);
92 	GAPrintHowTo(CtrlStr);
93 	exit(EXIT_FAILURE);
94     }
95 
96     if (HelpFlag) {
97 	(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
98 	GAPrintHowTo(CtrlStr);
99 	exit(EXIT_SUCCESS);
100     }
101 
102     /* Make sure intensities are in the right range: */
103     if (MinimumIntensity < 0 || MinimumIntensity > 100 ||
104 	MaximumIntensity < 0 || MaximumIntensity > 100)
105 	GIF_EXIT("Intensities (-m or -M options) are not in [0..100] range (percent).");
106 
107     /* Convert DirectionStr to our local representation: */
108     Direction = DIR_NONE;
109     FlipDir = false;
110      /* Make sure it's upper case. */
111     for (i = 0; i < (int)strlen(DirectionStr);  i++)
112 	if (islower(DirectionStr[i]))
113 	    DirectionStr[i] = toupper(DirectionStr[i]);
114 
115     switch(DirectionStr[0]) {
116 	case 'T': /* Top or North */
117 	case 'N':
118 	    if (strlen(DirectionStr) < 2)
119 		Direction = DIR_TOP;
120 	    else
121 		switch(DirectionStr[1]) {
122 		    case 'R':
123 		    case 'E':
124 			Direction = DIR_TOP_RIGHT;
125 			break;
126 		    case 'L':
127 		    case 'W':
128 			Direction = DIR_TOP_LEFT;
129 			FlipDir = true;
130 			break;
131 		}
132 	    break;
133 	case 'R': /* Right or East */
134 	case 'E':
135 	    Direction = DIR_RIGHT;
136 	    break;
137 	case 'B': /* Bottom or South */
138 	case 'S':
139 	    if (strlen(DirectionStr) < 2) {
140 		Direction = DIR_BOT;
141 		FlipDir = true;
142 	    }
143 	    else
144 		switch(DirectionStr[1]) {
145 		    case 'R':
146 		    case 'E':
147 			Direction = DIR_BOT_RIGHT;
148 			break;
149 		    case 'L':
150 		    case 'W':
151 			Direction = DIR_BOT_LEFT;
152 			FlipDir = true;
153 			break;
154 		}
155 	    break;
156 	case 'L': /* Left or West */
157 	case 'W':
158 	    Direction = DIR_LEFT;
159 	    FlipDir = true;
160 	    break;
161     }
162     if (Direction == DIR_NONE)
163 	GIF_EXIT("Direction requested (-d option) is weird!");
164 
165     /* We are going to handle only TOP, TOP_RIGHT, RIGHT, BOT_RIGHT  so flip */
166     /* the complement cases (TOP <-> BOT for example) by flipping the	     */
167     /* Color i with color (NumLevels - i - 1).				     */
168     if (FlipDir) {
169 	switch (Direction) {
170 	    case DIR_BOT:
171 		Direction = DIR_TOP;
172 		break;
173 	    case DIR_BOT_LEFT:
174 		Direction = DIR_TOP_RIGHT;
175 		break;
176 	    case DIR_LEFT:
177 		Direction = DIR_RIGHT;
178 		break;
179 	    case DIR_TOP_LEFT:
180 		Direction = DIR_BOT_RIGHT;
181 		break;
182 	}
183     }
184 
185     /* If binary mask is requested (special case): */
186     if (MinimumIntensity == 100 && MaximumIntensity == 100 && NumLevels == 2) {
187 	MinimumIntensity = 0;
188 	DoAllMaximum = true;
189 	Direction = DIR_RIGHT;
190     }
191 
192     /* Make sure colors are in the right range: */
193     if (RedColor > 255 || GreenColor > 255 || BlueColor > 255)
194 	GIF_EXIT("Colors are not in the ragne [0..255].");
195 
196     /* Make sure number of levels is power of 2 (up to 8 bits per pixel).    */
197     for (i = 1; i < 8; i++) if (NumLevels == (1 << i)) break;
198     if (i == 8) GIF_EXIT("#Lvls (-l option) is not power of 2.");
199     LogNumLevels = i;
200 
201     /* Open stdout for the output file: */
202     if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
203 	PrintGifError(ErrorCode);
204 	exit(EXIT_FAILURE);
205     }
206 
207     /* Dump out screen description with given size and generated color map:  */
208     if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL)
209 	GIF_EXIT("Failed to allocate memory required, aborted.");
210 
211     for (i = 1; i <= NumLevels; i++) {
212 	/* Ratio will be in the range of 0..100 for required intensity: */
213 	unsigned int Ratio = (MaximumIntensity * (i * (256 / NumLevels)) +
214 			      MinimumIntensity * ((NumLevels - i) * (256 / NumLevels))) /
215 	    256;
216 	ColorMap->Colors[i-1].Red   = (RedColor * Ratio) / 100;
217 	ColorMap->Colors[i-1].Green = (GreenColor * Ratio) / 100;
218 	ColorMap->Colors[i-1].Blue  = (BlueColor * Ratio) / 100;
219     }
220     if (EGifPutScreenDesc(GifFile,
221 	ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap)
222 	== GIF_ERROR)
223 	QuitGifError(GifFile);
224 
225     /* Dump out the image descriptor: */
226     if (EGifPutImageDesc(GifFile,
227 	0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR)
228 	QuitGifError(GifFile);
229 
230     GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]:     ",
231 	       PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top,
232 	       GifFile->Image.Width, GifFile->Image.Height);
233 
234     /* Allocate one scan line twice as big as image is, as we are going to   */
235     /* shift along it, while we dump the scan lines:			     */
236     if ((Line = (GifRowType) malloc(sizeof(GifPixelType) * ImageWidth * 2)) == NULL)
237 	GIF_EXIT("Failed to allocate memory required, aborted.");
238 
239     if (Direction == DIR_TOP) {
240 	int LevelHeight;
241 	/* We must evaluate the line each time level is changing: */
242 	LevelHeight = ImageHeight / NumLevels;
243 	for (Color = NumLevels, i = l = 0; i < ImageHeight; i++) {
244 	    if (i == l) {
245 		int j;
246 		/* Time to update the line to next color level: */
247 		if (Color != 0) Color--;
248 		for (j = 0; j < ImageWidth; j++)
249 		    Line[j] = (FlipDir ? NumLevels - Color - 1 : Color);
250 		l += LevelHeight;
251 	    }
252 	    if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR)
253 		QuitGifError(GifFile);
254 	    GifQprintf("\b\b\b\b%-4d", Count++);
255 	}
256     }
257     else if (Direction == DIR_RIGHT) {
258 	/* We pre-prepare the scan lines as going from color zero to maximum */
259 	/* color and dump the same scan line Height times:		     */
260 	/* Note this case should handle the Boolean Mask special case.	     */
261 	LevelWidth = ImageWidth / NumLevels;
262 	if (DoAllMaximum) {
263 	    /* Special case - do all in maximum color: */
264 	    for (i = 0; i < ImageWidth; i++) Line[i] = 1;
265 	}
266 	else {
267 	    for (Color = i = 0, l = LevelWidth; i < ImageWidth; i++, l--) {
268 		if (l == 0) {
269 		    l = LevelWidth;
270 		    if (Color < NumLevels - 1) Color++;
271 		}
272 		Line[i] = (FlipDir ? NumLevels - Color - 1 : Color);
273 	    }
274 	}
275 
276 	for (i = 0; i < ImageHeight; i++) {
277 	    /* coverity[uninit_use_in_call] */
278 	    if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR)
279 		QuitGifError(GifFile);
280 	    GifQprintf("\b\b\b\b%-4d", Count++);
281 	}
282     }
283     else {
284 	int Accumulator, StartX, StepX;
285 	/* We are in one of the TOP_RIGHT, BOT_RIGHT cases: we will          */
286 	/* initialize the Line with its double ImageWidth length from the    */
287 	/* minimum intensity to the maximum intensity and shift along it     */
288 	/* while we go along the image height.				     */
289 	LevelWidth = ImageWidth * 2 / NumLevels;
290 	for (Color = i = 0, l = LevelWidth; i < ImageWidth * 2; i++, l--) {
291 	    if (l == 0) {
292 		l = LevelWidth;
293 		if (Color < NumLevels - 1) Color++;
294 	    }
295 	    Line[i] = (FlipDir ? NumLevels - Color - 1 : Color);
296 	}
297 	/* We need to implement a DDA to know how much to shift Line while   */
298 	/* we go down along image height. we set the parameters for it now:  */
299 	Accumulator = 0;
300 	switch(Direction) {
301 	    case DIR_TOP_RIGHT:
302 		StartX = ImageWidth;
303 		StepX = -1;
304 		break;
305 	    case DIR_BOT_RIGHT:
306 	    default:
307 		StartX = 0;
308 		StepX = 1;
309 		break;
310 	}
311 
312 	/* Time to dump information out: */
313 	for (i = 0; i < ImageHeight; i++) {
314 	    if (EGifPutLine(GifFile, &Line[StartX], ImageWidth) == GIF_ERROR)
315 		QuitGifError(GifFile);
316 	    GifQprintf("\b\b\b\b%-4d", Count++);
317 	    if ((Accumulator += ImageWidth) > ImageHeight) {
318 		while (Accumulator > ImageHeight) {
319 		    Accumulator -= ImageHeight;
320 		    StartX += StepX;
321 		}
322 		if (Direction < 0) Direction = 0;
323 		if (Direction > ImageWidth) Direction = ImageWidth;
324 	    }
325 	}
326     }
327 
328     if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR)
329     {
330 	PrintGifError(ErrorCode);
331 	exit(EXIT_FAILURE);
332     }
333 
334     return 0;
335 }
336 
337 /******************************************************************************
338  Close output file (if open), and exit.
339 ******************************************************************************/
QuitGifError(GifFileType * GifFile)340 static void QuitGifError(GifFileType *GifFile)
341 {
342     if (GifFile != NULL) {
343 	PrintGifError(GifFile->Error);
344 	EGifCloseFile(GifFile, NULL);
345     }
346     exit(EXIT_FAILURE);
347 }
348 
349 /* end */
350