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