1 /****************************************************************
2  * amain.c
3  *
4  * Copyright (C) 1995-2002 Klaus Ehrenfried
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  *************************************************************************/
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include "apro.h"
25 
26 static const char ppm2fli_env_var[]="PPM2FLIFILTER";
27 static int output_file_created;
28 static char *output_name;
29 
30 #define SCANINT(x,y) \
31 if (sscanf(x,"%d",y) != 1) \
32 { fprintf(stderr,"Invalid number '%s' in argument %d\n",x,i);\
33  print_hint(); return(1); }
34 
35 #define GETNUMBER(x) \
36 ppa++; \
37 if (*ppa == '\0') { pending_number = &(x); pp_flag = 1; } \
38 else { SCANINT(ppa,&(x)); }
39 
40 #define GETNAME(x) \
41 ppa++; \
42 if (*ppa == '\0') { pending_name = &(x); pp_flag = 2; } \
43 else { x = ppa; }
44 
45 
46 /****************************************************************
47  * print_usage
48  ****************************************************************/
49 
print_usage()50 static int print_usage()
51 {
52   fprintf(stdout,"PPM2FLI version 2.1 by Klaus Ehrenfried (C) 1995,2002\n");
53   fprintf(stdout,"Usage: ppm2fli [options] list-file fli-file\n");
54 
55   return(1);
56 }
57 
58 /****************************************************************
59  * print_hint()
60  ****************************************************************/
61 
print_hint()62 static int print_hint()
63 {
64   fprintf(stderr,"Type 'ppm2fli -h' for help\n");
65   return(1);
66 }
67 
68 /****************************************************************
69  * print_usage
70  ****************************************************************/
71 
print_help()72 static int print_help()
73 {
74   fprintf(stdout,"Function:\n\
75   Reads from 'list-file' line by line file names of PPM/PGM/PBM/FBM images,\n\
76   generates a common color-table by scanning all images,\n\
77   quantizes the images and assembles a FLI/FLC animation,\n\
78   which is written to 'fli-file'\n");
79 
80   fprintf(stdout,"Quantize options:\n\
81   -I                  Individual quantize: Don't use common color table,\n\
82                         generate for each image separate table\n\
83   -Qc<colors>         Set max number of colors (9 - 256; default = 256)\n\
84   -Qd<depth>          Set color depth (2 - 8; default = 8)\n\
85   -Qn<limit>          Set node limit in Octree (16 - 2048; default = 512)\n\
86   -Qr<value>          Set max number of reduce levels (0 - 8; default = 8)\n\
87   -m<file>            Don't scan all images previously, generate color-table\n\
88                         by scanning only the image in specified file\n\
89                         (ppm or fbm, read filter is ineffective)\n\
90   -w<file>            Write generated color-table to file (256x1 PPM ascii)\n\
91                         and stop, don't make FLI/FLC\n");
92 
93   fprintf(stdout,"FLI/FLC options:\n\
94   -D                  Double buffer optimization (for special players)\n\
95   -N                  Reverse play suitability (nice when using XAnim)\n\
96   -O                  Generate old format FLI instead of FLC\n\
97   -b<color>           Set border color (default = 0)\n\
98   -g<width>x<height>  Set width and height (10x10 - 1280x1024),\n\
99                         default is 640x480 (320x200 when using '-O')\n\
100   +/-ox<pos>          Switch off horizontal centering of input images,\n\
101                         locate the left/right border at specified position\n\
102   +/-oy<pos>          Switch off vertical centering of input images,\n\
103                         locate the upper/lower border at specified position\n\
104   -s<speed>           Set play speed stored in FLI/FLC file\n");
105 
106   fprintf(stdout,"General options:\n\
107   -v                  Verbose ('-vv' is more verbose)\n\
108   +/-f<filter>        Read all input images via the specified filter\n\
109                         (e.g. '-fgunzip', '-fgiftopnm', '-ffbcat', etc.),\n\
110                         '+f' filter needs file name as argument\n\
111                         '-f' filter reads from stdin\n\
112   -t                  Test file magic: use read filter only for files\n\
113                         without PPM,PGM,PBM or FBM magic\n");
114   fprintf(stdout,"For more details, please read the manual pages\n");
115   return(1);
116 }
117 
118 /****************************************************************
119  * exitialise
120  ****************************************************************/
121 
exitialise(int error_flag)122 int exitialise(int error_flag)
123 {
124     if (error_flag != 0)
125     {
126 	if (output_file_created == 1)
127 	{
128 	  if (output != NULL) fclose(output);
129 
130 	  fprintf(stderr,"Remove incomplete output file '%s'\n",output_name);
131 	  (void) remove(output_name);
132 	}
133 	fprintf(stderr,"Abnormal termination\n");
134     }
135     return(1);
136 }
137 
138 /****************************************************************
139  * main
140  ****************************************************************/
141 
main(int argc,char * argv[])142 int main (int argc, char *argv[])
143 {
144   FILE *fopen();
145   char *listfile, *ppa, *ppb, *map_file, *output_pal_file, *geom;
146   char **pending_name;
147   char abuff[10];
148   int answer_flag, max_chunk_size;
149   int s_speed;
150   int i, itest, pp_flag, list_input_flag;
151   int *pending_number;
152   int help_filter_flag;
153   int help_origin;
154 
155   /* octree defaults */
156   max_colors=FLI_MAX_COLORS;
157   node_limit = 2 * FLI_MAX_COLORS;
158   reduce_dynamics=MAXDEPTH;
159   color_depth=8;
160 
161   /* -- -- - - -- -- */
162   tmp_file_name = NULL;
163   verbose_flag = 0;
164 
165   geom=NULL;
166   listfile=NULL;
167   output_name=NULL;
168   map_file=NULL;
169   input=0;
170   output=0;
171 
172   output_file_created=0;
173 
174   pixel_chunk_buffer=NULL;
175 
176   Xorigin=0;
177   Xorigin_flag=0;
178   Yorigin=0;
179   Yorigin_flag=0;
180   s_speed=-1;
181 
182   border_color=0;
183   map_color_flag=0;
184   use_next_flag=0;
185   double_buffer=0;
186   old_format_flag=0;
187 
188   write_pal_flag = 0;
189   individual_flag = 0;
190   filter_flag = 0;
191   help_filter_flag = 0;
192   test_magic_flag = 0;
193 
194   wobbel_flag=0;
195 
196   /* scan arguments */
197 
198   pp_flag = 0;
199   pending_number = NULL;
200   pending_name = NULL;
201   ppa = ppb = NULL;
202 
203   if (argc == 1)
204     { print_usage(); return(1); }
205 
206   for (i=1; i < argc; i++)
207     {
208       ppa=argv[i];
209       if (pp_flag == 1)
210 	{ SCANINT(ppa, pending_number); pp_flag = 0; }
211       else if (pp_flag == 2)
212 	{ *pending_name = ppa; pp_flag = 0; }
213       else if ((*ppa == '-') || (*ppa == '+'))
214 	{
215 	  ppb = (ppa++);
216 	  switch (*ppa)
217 	    {
218 	    case 'h':   print_usage(); print_help(); return(1);
219 	    case 'b':   GETNUMBER(border_color); break;
220 	    case 'D':   double_buffer=1; break;
221 	    case 'g':   GETNAME(geom); break;
222 	    case 'f':
223 	      GETNAME(filter_name);
224 	      if (*ppb == '+')
225 		{help_filter_flag = 2;}
226 	      else
227 		{help_filter_flag = 1;}
228 	      break;
229 	    case 'I':   individual_flag=1; break;
230 	    case 'm':   GETNAME(map_file); map_color_flag=1; break;
231 	    case 'N':   use_next_flag=1; break;
232 	    case 'O':   old_format_flag=1; break;
233 	    case 'o':
234 	      if (*ppb == '+')
235 		{help_origin = 1;}
236 	      else
237 		{help_origin = 2;}
238 	      ppa++;
239 	      if (*ppa == 'x')
240 		{ GETNUMBER(Xorigin); Xorigin_flag = help_origin;}
241 	      else if (*ppa == 'y')
242 		{ GETNUMBER(Yorigin); Yorigin_flag = help_origin;}
243 	      else
244 		{ goto invalid_option;}
245 	      break;
246 	    case 'Q':
247 	      ppa++;
248 	      if (*ppa == 'c')
249 		{ GETNUMBER(max_colors);}
250 	      else if (*ppa == 'd')
251 		{ GETNUMBER(color_depth);}
252 	      else if (*ppa == 'n')
253 		{ GETNUMBER(node_limit);}
254 	      else if (*ppa == 'r')
255 		{ GETNUMBER(reduce_dynamics);}
256 	      else
257 		{ goto invalid_option;}
258 	      break;
259 	    case 's':   GETNUMBER(s_speed); break;
260 	    case 't':   test_magic_flag = 1; break;
261 	    case 'v':
262 	      ppa++;
263 	      if (*ppa == 'v') {verbose_flag = 2;} else {verbose_flag = 1;}
264 	      break;
265 	    case 'w':   GETNAME(output_pal_file); write_pal_flag=1; break;
266 	    case 'W':   wobbel_flag = 1; break;
267 	    invalid_option:
268 	    default:
269 	      fprintf(stderr,"Invalid option '%s'\n",ppb);
270 	      print_hint();
271 	      return(1);
272 	    }
273 	}
274       else if (listfile == NULL)
275 	{
276 	  listfile=ppa;
277 	}
278       else if (output_name == NULL)
279 	{
280 	  output_name=ppa;
281 	}
282       else
283 	{
284 	  fprintf(stderr,"Too many parameters specified\n");
285 	  print_hint();
286 	  return(1);
287 	}
288     }
289 
290   if (pp_flag != 0)
291     {
292       fprintf(stderr,"Missing parameter behind option '%s'\n",ppb);
293       print_hint();
294       return(1);
295     }
296 
297   /* -- check if all necessary file names are given -- */
298 
299   if ((map_color_flag) && (write_pal_flag))
300     { list_input_flag = 0; }
301   else
302     { list_input_flag = 1; }
303 
304   if (listfile == NULL)
305     {
306       if (list_input_flag)
307 	{
308 	  fprintf(stderr,"No list-file specified\n");
309 	  print_hint();
310 	  return(1);
311 	}
312     }
313   else
314     {
315       if (!list_input_flag)
316 	{
317 	  fprintf(stderr,"No list-file required: file name '%s' ignored\n",
318 		  listfile);
319 	}
320     }
321 
322   if (output_name == NULL)
323     {
324       if (!write_pal_flag)
325 	{
326 	  fprintf(stderr,"No FLI-file specified\n");
327 	  print_hint();
328 	  return(1);
329 	}
330     }
331   else
332     {
333       if (write_pal_flag)
334 	{
335 	  fprintf(stderr,"Only write color table: file name '%s' ignored\n",
336 		  output_name);
337 	}
338     }
339 
340   /* --- check for incompatibilities --- */
341 
342   if (use_next_flag)
343     {
344       if (double_buffer)
345 	{
346 	  fprintf(stderr,"Invalid combination of '-D' and '-N'\n");
347 	  print_hint();
348 	  return (1);
349 	}
350       double_buffer = 1;
351     }
352 
353   if (individual_flag)
354     {
355       if (map_color_flag)
356 	{
357 	  fprintf(stderr,"Invalid combination of '-I' and '-m <file>'\n");
358 	  print_hint();
359 	  return (1);
360 	}
361       if (write_pal_flag)
362 	{
363 	  fprintf(stderr,"Invalid combination of '-I' and '-w <file>'\n");
364 	  print_hint();
365 	  return (1);
366 	}
367     }
368 
369   /* --- check options for quantize --- */
370 
371   if ((node_limit < MIN_NODE_LIMIT) || (node_limit > MAX_NODE_LIMIT))
372     {
373       fprintf(stderr,"Invalid node limit specified: %d\n",node_limit);
374       print_hint();
375       return (1);
376     }
377 
378   if (color_depth > 8 || color_depth < 2)
379     {
380       fprintf (stderr,"Invalid color depth specified: %d\n",color_depth);
381       print_hint();
382       return (1);
383     }
384 
385   if (max_colors > 256 || max_colors < 9)
386     {
387       fprintf (stderr, "Invalid number of colors specified: %d\n",
388 	       max_colors);
389       print_hint();
390       return (1);
391     }
392 
393   if (reduce_dynamics < 0) reduce_dynamics = 0;
394 
395   /* --- output format --- */
396 
397   if (write_pal_flag)
398     {
399       output_name = output_pal_file;
400     }
401   else
402     {
403       if (old_format_flag == 1)
404 	{
405 	  fli_width=320;
406 	  fli_height=200;
407 	  fli_speed=5;
408 	}
409       else
410 	{
411 	  fli_width=640;
412 	  fli_height=480;
413 	  fli_speed=72;
414 	}
415 
416       if (geom != NULL)
417 	{
418 	  if ((sscanf(geom,"%dx%d",&fli_width,&fli_height)) != 2)
419 	    {
420 	      fprintf (stderr,"Invalid geometry: '%s'\n",geom);
421 	      print_hint();
422 	      return (1);
423 	    }
424 
425 	  if ((fli_width % 2) == 1) fli_width++;            /* no odd width */
426 	  if ((fli_width < 10) || (fli_width > FLI_MAX_X))
427 	    {
428 	      fprintf (stderr,"Invalid width: %d\n",fli_width);
429 	      print_hint();
430 	      return (1);
431 	    }
432 	  if ((fli_height < 10) || (fli_height > FLI_MAX_Y))
433 	    {
434 	      fprintf (stderr,"Invalid height: %d\n",fli_height);
435 	      print_hint();
436 	      return (1);
437 	    }
438 	}
439 
440       fli_size = fli_width * fli_height;
441 
442       if (s_speed >= 0)           /* be tolerant */
443 	fli_speed = s_speed;
444     }
445 
446   if (list_input_flag)
447     {
448       if ((input = fopen(listfile, "r")) == NULL)
449 	{
450 	  fprintf(stderr,"Error opening list-file '%s'\n",listfile);
451 	  return(1);
452 	}
453     }
454   else
455     {
456       input = NULL;
457     }
458 
459   if (verbose_flag > 0)
460     {
461       fprintf(stdout,"Output:     '%s'\n",output_name);
462     }
463 
464   if (check_exist(output_name) == 1)
465     {
466       fprintf(stderr,"%s already exists\n",output_name);
467       answer_flag=0;
468       while (answer_flag == 0)
469 	{
470 	  fprintf(stderr," overwrite %s (y/n) ",output_name);
471 	  fflush(stdout);
472 	  if ((itest=get_next_line(stdin, abuff, 2)) == 1)
473 	    {
474 	      if ((abuff[0] == 'y') || (abuff[0] == 'Y'))
475 		answer_flag=1;
476 	      else if ((abuff[0] == 'n') || (abuff[0] == 'N'))
477 		answer_flag=2;
478 	    }
479 	}
480       if (answer_flag != 1) return(1);
481     }
482 
483   /* ............. open output file ........ */
484 
485   if (write_pal_flag)
486     {
487       if ((output = fopen(output_name, "w")) == NULL)
488 	{
489 	  fprintf(stderr,"Error opening color table file '%s'\n",output_name);
490 	  return(1);
491 	}
492       output_file_created = 1;
493     }
494   else
495     {
496       if ((output = fopen(output_name, "wb")) == NULL)
497 	{
498 	  fprintf(stderr,"Error opening fli-file '%s'\n",output_name);
499 	  return(1);
500 	}
501       output_file_created = 1;
502 
503       if (border_color > 0x00FF) border_color=0x00FF;
504       if (border_color < 0x0000) border_color=0x0000;
505 
506       if (verbose_flag > 0)
507 	{
508 	  fprintf(stdout," Resolution: %dx%d\n",fli_width,fli_height);
509 	  fprintf(stdout," Origin:     %dx%d\n",Yorigin,Xorigin);
510 	  fprintf(stdout," Speed:      %d\n",fli_speed);
511 	}
512 
513       max_chunk_size=fli_height*(2*fli_width+10)+1024;
514 
515       pixel_chunk_buffer = malloc(max_chunk_size);
516       if (pixel_chunk_buffer == NULL)
517 	{
518 	  fprintf(stderr,"Can't allocate %d bytes\n",max_chunk_size);
519 	  exitialise(1);
520 	  return(1);
521 	}
522     }
523 
524   if (map_color_flag == 1)
525     {
526       fprintf(stdout,"Generate color table from file '%s'\n",map_file);
527       clear_octree();
528       scan_rgb_image(map_file);
529       prepare_quantize();
530     }
531 
532   if (help_filter_flag == 0)
533     {
534       filter_name = getenv(ppm2fli_env_var);
535       if (filter_name != NULL)
536 	{
537 	  help_filter_flag = 1;
538 	  if (*filter_name == '-')
539 	    {
540 	      filter_name++;
541 	    }
542 	  else if (*filter_name == '+')
543 	    {
544 	      filter_name++;
545 	      help_filter_flag = 2;
546 	    }
547 	}
548     }
549 
550   filter_flag = help_filter_flag;
551 
552   if (make_fli() < 0)
553     {
554       exitialise(1);
555       return(1);
556     }
557 
558   fprintf(stdout,"Ready\n");
559   exitialise(0);
560   return(0);
561 }
562 
563 /* -- FIN -- */
564