1 /* Convert xcf files to ppm
2  *
3  * This file was written by Henning Makholm <henning@makholm.net>
4  * It is hereby in the public domain.
5  *
6  * In jurisdictions that do not recognise grants of copyright to the
7  * public domain: I, the author and (presumably, in those jurisdictions)
8  * copyright holder, hereby permit anyone to distribute and use this code,
9  * in source code or binary form, with or without modifications. This
10  * permission is world-wide and irrevocable.
11  *
12  * Of course, I will not be liable for any errors or shortcomings in the
13  * code, since I give it away without asking any compenstations.
14  *
15  * If you use or distribute this code, I would appreciate receiving
16  * credit for writing it, in whichever way you find proper and customary.
17  */
18 
19 #include "xcftools.h"
20 #include "flatten.h"
21 #include "nlsini.h"
22 #include "options.h"
23 #include "version.h"
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <locale.h>
28 #include <ctype.h>
29 #include <getopt.h>
30 
31 static int suppress_byline ;
32 static struct FlattenSpec flatspec ;
33 static FILE *outfile = NULL ;
34 static FILE *transfile = NULL ;
35 
36 /*----------------------------------------------------------------------------*/
37 const struct option long_options[] = {
38   option_help,
39   option_version,
40   option_verbose,
41   option_bzip,
42   option_gzip,
43   option_unpack,
44   option_output,
45   option_alpha,
46   option_background,
47   option_force_alpha,
48   option_color,
49   option_colour,
50   option_gray,
51   option_grey,
52   option_mono,
53   option_pnm,
54   option_truecolor,
55   option_for_gif,
56   option_dissolve,
57   option_full_image,
58   option_size,
59   option_offset,
60   option_autocrop,
61   option_mode,
62   option_percent,
63   option_opacity,
64   option_mask,
65   option_nomask,
66   option_utf8,
67   { 0 }
68 };
69 
70 /*----------------------------------------------------------------------------*/
71 const char* const short_options = short_options_prefix
72   short_option_help
73   short_option_version
74   short_option_verbose
75   short_option_bzip
76   short_option_gzip
77   short_option_unpack
78   short_option_output
79   short_option_alpha
80   short_option_background
81   short_option_force_alpha
82   short_option_color
83   short_option_gray
84   short_option_mono
85   short_option_pnm
86   short_option_truecolor
87   short_option_for_gif
88   short_option_dissolve
89   short_option_full_image
90   short_option_size
91   short_option_offset
92   short_option_autocrop
93   short_option_utf8
94   ;
95 
96 static void
start_writing(FILE ** f,int version)97 start_writing(FILE **f,int version)
98 {
99   const char *format[] = {"(format zero)",
100                           "PBM-ascii",
101                           "PGM-ascii",
102                           "PPM-ascii",
103                           "PBM",
104                           "PGM",
105                           "PPM" };
106 
107   if( verboseFlag )
108     fprintf(stderr,f == &outfile ? gettext("Writing converted image as %s\n")
109             : gettext("Writing transparency map as %s\n"),
110             format[version]);
111 
112   *f = openout( f == &outfile ? flatspec.output_filename
113                 : flatspec.transmap_filename );
114   fprintf(*f,"P%d",version);
115   if( suppress_byline )
116     ;
117   else if( f == &outfile )
118     fprintf(*f,gettext(" # Converted by xcf2pnm %s"), PACKAGE_STRING);
119   else
120     fprintf(*f,gettext(" # Transparency map by xcf2pnm %s"), PACKAGE_STRING);
121   fprintf(*f,"\n%d %d\n%s",
122           flatspec.dim.width,
123           flatspec.dim.height,
124           version == 4 ? "" : "255\n");
125 }
126 
127 int
put_pbm_row(FILE * file,unsigned num,rgba * pixels,rgba mask)128 put_pbm_row(FILE *file,unsigned num,rgba *pixels,rgba mask) {
129   unsigned out ;
130   unsigned i ;
131   int bitsleft = 8 ;
132   out = 0 ;
133   for( i=0; i<num; i++ ) {
134     out <<= 1 ;
135     if( (pixels[i] & mask) == 0 )
136       out++ ; /* 1 is black */
137     else if( (pixels[i] & mask) == mask )
138       ; /* 0 is white */
139     else
140       return 0 ;
141     if( --bitsleft == 0 ) {
142       putc( out, file );
143       out = 0 ;
144       bitsleft = 8 ;
145     }
146   }
147   if( bitsleft < 8 )
148     putc( out << bitsleft, file );
149   return 1 ;
150 }
151 
152 static void
callback_common(unsigned num,rgba * pixels)153 callback_common(unsigned num,rgba *pixels)
154 {
155   if( flatspec.transmap_filename ) {
156     if( flatspec.partial_transparency_mode == ALLOW_PARTIAL_TRANSPARENCY ) {
157       unsigned i ;
158       if( transfile == NULL ) start_writing(&transfile,5);
159       for( i=0; i < num; i++ )
160         putc( ALPHA(pixels[i]), transfile );
161     } else {
162       if( transfile == NULL ) {
163         start_writing(&transfile,4);
164       }
165       /* Partial transparency should have been caught in the flattener,
166        * so just extract a single byte.
167        */
168       put_pbm_row(transfile,num,pixels,(rgba)1 << ALPHA_SHIFT);
169     }
170   } else if( ALPHA(flatspec.default_pixel) < 128 ) {
171     unsigned i ;
172     for( i=0; i < num; i++ )
173       if( !FULLALPHA(pixels[i]) )
174         FatalGeneric
175           (100, gettext("Transparency found, but -a option not given"));
176   }
177   xcffree(pixels) ;
178 }
179 
180 static void
ppm_callback(unsigned num,rgba * pixels)181 ppm_callback(unsigned num,rgba *pixels)
182 {
183   unsigned i ;
184   if( outfile == NULL ) start_writing(&outfile,6);
185   for( i=0; i < num; i++ ) {
186     putc( (pixels[i] >> RED_SHIFT) & 0xFF   , outfile );
187     putc( (pixels[i] >> GREEN_SHIFT) & 0xFF , outfile );
188     putc( (pixels[i] >> BLUE_SHIFT) & 0xFF  , outfile );
189   }
190   callback_common(num,pixels);
191 }
192 
193 static void
pgm_callback(unsigned num,rgba * pixels)194 pgm_callback(unsigned num,rgba *pixels)
195 {
196   unsigned i ;
197   if( outfile == NULL ) start_writing(&outfile,5);
198   for( i=0; i < num; i++ ) {
199     int gray = degrayPixel(pixels[i]) ;
200     if( gray == -1 )
201       FatalGeneric
202         ( 103,
203           gettext("Grayscale output selected, but colored pixel(s) found") );
204     putc( gray, outfile );
205   }
206   callback_common(num,pixels);
207 }
208 
209 static void
pbm_callback(unsigned num,rgba * pixels)210 pbm_callback(unsigned num,rgba *pixels)
211 {
212   if( outfile == NULL ) start_writing(&outfile,4);
213   if( !put_pbm_row(outfile,num,pixels,
214                    ((rgba)255 << RED_SHIFT) +
215                    ((rgba)255 << GREEN_SHIFT) +
216                    ((rgba)255 << BLUE_SHIFT)) )
217     FatalGeneric
218       ( 103,
219         gettext( "Monochrome output selected, but not all pixels "
220                  "are black or white" ) );
221   callback_common(num,pixels);
222 }
223 
224 static enum out_color_mode
guess_color_mode(const char * string)225 guess_color_mode(const char *string)
226 {
227   if( strlen(string) >= 3 ) {
228     string += strlen(string)-3 ;
229     if( strcmp(string,"ppm")==0 ) return COLOR_RGB ;
230     if( strcmp(string,"pgm")==0 ) return COLOR_GRAY ;
231     if( strcmp(string,"pbm")==0 ) return COLOR_MONO ;
232   }
233   return COLOR_BY_FILENAME ;
234 }
235 
236 static lineCallback
selectCallback(void)237 selectCallback(void)
238 {
239   if( flatspec.transmap_filename && ALPHA(flatspec.default_pixel) >= 128 )
240     FatalGeneric(101,gettext("The -a option was given, "
241                        "but the image has no transparency"));
242 
243   switch( flatspec.out_color_mode ) {
244   default:
245   case COLOR_RGB: return &ppm_callback ;
246   case COLOR_GRAY: return &pgm_callback ;
247   case COLOR_MONO: return &pbm_callback ;
248   }
249 }
250 
251 int
main(int argc,char ** argv)252 main(int argc,char **argv)
253 {
254   struct ProcessControl process;
255 
256   setlocale(LC_ALL,"");
257   progname = argv[0] ;
258   nls_init();
259 
260   if( argc <= 1 ) gpl_blurb() ;
261 
262   init_flatspec(&flatspec) ;
263   init_process_control( &process );
264 
265   flatspec.out_color_mode = COLOR_BY_FILENAME ;
266 
267   if ( option_parse
268        ( argc, argv, short_options, long_options, &process, &flatspec ) )
269     exit(1);
270 
271   // set the global flags
272   verboseFlag = process.verboseFlag;
273   use_utf8 = process.use_utf8;
274 
275   if( flatspec.out_color_mode == COLOR_BY_FILENAME &&
276       strlen(flatspec.output_filename) > 4 &&
277       flatspec.output_filename[strlen(flatspec.output_filename)-4] == '.' )
278     flatspec.out_color_mode = guess_color_mode(flatspec.output_filename);
279 
280   /* If the output filename was not enough cue, see if we're running
281    * through a symlink/hardlink that gives the required output format
282    */
283   if( flatspec.out_color_mode == COLOR_BY_FILENAME &&
284       strlen(progname) > 3 )
285     flatspec.out_color_mode = guess_color_mode(progname);
286 
287   if( flatspec.out_color_mode == COLOR_BY_FILENAME )
288     flatspec.out_color_mode = COLOR_BY_CONTENTS ;
289 
290   read_or_mmap_xcf( process.inputFile, process.unzipper );
291   getBasicXcfInfo() ;
292   initColormap();
293 
294   complete_flatspec(&flatspec,NULL);
295   if( flatspec.process_in_memory ) {
296     rgba **allPixels = flattenAll(&flatspec);
297     analyse_colormode(&flatspec,allPixels,NULL);
298     shipoutWithCallback(&flatspec,allPixels,selectCallback());
299   } else {
300     flattenIncrementally(&flatspec,selectCallback());
301   }
302   closeout(outfile,flatspec.output_filename) ;
303   closeout(transfile,flatspec.transmap_filename) ;
304   return 0 ;
305 }
306