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