]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Refactor option processing
[ttf2png.git] / ttf2png.c
1 /*
2 ttf2png - True Type Font to PNG converter
3 Copyright (c) 2004-2008 Mikko Rasa
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <getopt.h>
24 #include <png.h>
25 #include <ft2build.h>
26 #include FT_FREETYPE_H
27
28 typedef struct sImage
29 {
30         unsigned w, h;
31         char *data;
32 } Image;
33
34 typedef struct sGlyph
35 {
36         unsigned index;
37         unsigned code;
38         Image image;
39         unsigned x, y;
40         int offset_x;
41         int offset_y;
42         int advance;
43 } Glyph;
44
45 typedef struct sKerning
46 {
47         unsigned left_code;
48         unsigned right_code;
49         int distance;
50 } Kerning;
51
52 typedef struct sFont
53 {
54         unsigned size;
55         int ascent;
56         int descent;
57         unsigned n_glyphs;
58         Glyph *glyphs;
59         unsigned n_kerning;
60         Kerning *kerning;
61         Image image;
62 } Font;
63
64 void usage(void);
65 int convert_numeric_option(char, int);
66 void convert_code_point_range(char, int *, int *);
67 void convert_size(char, int *, int *);
68 unsigned round_to_pot(unsigned);
69 void *alloc_image_data(size_t, size_t);
70 int init_font(Font *, FT_Face, unsigned, unsigned, int);
71 int render_grid(Font *, unsigned, unsigned, unsigned, int);
72 int render_packed(Font *, unsigned, unsigned);
73 int save_defs(const char *, const Font *);
74 int save_png(const char *, const Image *, char);
75
76 char verbose = 0;
77
78 int main(int argc, char **argv)
79 {
80         char *fn;
81         int begin = 0;
82         int end = 255;
83         int size = 10;
84         int cpl = 0;
85         int cellw = 0;
86         int cellh = 0;
87         char autohinter = 0;
88         char seq = 0;
89         char alpha = 0;
90         char invert = 0;
91         char pack = 0;
92         int margin = 0;
93         int padding = 1;
94
95         FT_Library freetype;
96         FT_Face face;
97
98         int err;
99         int i;
100
101         char *out_fn = "font.png";
102         char *def_fn = NULL;
103
104         Font font;
105
106         if(argc<2)
107         {
108                 usage();
109                 return 1;
110         }
111
112         while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1)
113         {
114                 switch(i)
115                 {
116                 case 'r':
117                         convert_code_point_range('r', &begin, &end);
118                         break;
119                 case 's':
120                         size = convert_numeric_option('s', 1);
121                         break;
122                 case 'l':
123                         cpl = convert_numeric_option('l', 1);
124                         break;
125                 case 'c':
126                         convert_size('c', &cellw, &cellh);
127                         break;
128                 case 'o':
129                         out_fn = optarg;
130                         break;
131                 case 'a':
132                         autohinter = 1;
133                         break;
134                 case 't':
135                         alpha = 1;
136                         break;
137                 case 'v':
138                         ++verbose;
139                         break;
140                 case 'h':
141                 case '?':
142                         usage();
143                         return 0;
144                 case 'e':
145                         seq = 1;
146                         break;
147                 case 'd':
148                         def_fn = optarg;
149                         break;
150                 case 'p':
151                         pack = 1;
152                         break;
153                 case 'i':
154                         invert = 1;
155                         break;
156                 case 'm':
157                         margin = convert_numeric_option('m', 0);
158                         break;
159                 case 'n':
160                         padding = convert_numeric_option('n', 0);
161                         break;
162                 }
163         }
164         if(!strcmp(out_fn, "-"))
165                 verbose = 0;
166
167         if(optind!=argc-1)
168         {
169                 usage();
170                 return 1;
171         }
172
173         fn = argv[optind];
174
175         err = FT_Init_FreeType(&freetype);
176         if(err)
177         {
178                 fprintf(stderr, "Couldn't initialize FreeType library\n");
179                 return 1;
180         }
181
182         err = FT_New_Face(freetype, fn, 0, &face);
183         if(err)
184         {
185                 fprintf(stderr, "Couldn't load font file\n");
186                 if(err==FT_Err_Unknown_File_Format)
187                         fprintf(stderr, "Unknown file format\n");
188                 return 1;
189         }
190
191         if(verbose)
192         {
193                 const char *name = FT_Get_Postscript_Name(face);
194                 printf("Font name: %s\n", name);
195                 printf("Glyphs:    %ld\n", face->num_glyphs);
196         }
197
198         err = FT_Set_Pixel_Sizes(face, 0, size);
199         if(err)
200         {
201                 fprintf(stderr, "Couldn't set size\n");
202                 return 1;
203         }
204
205         font.size = size;
206         err = init_font(&font, face, begin, end, autohinter);
207         if(err)
208                 return 1;
209
210         if(pack)
211                 err = render_packed(&font, margin, padding);
212         else
213                 err = render_grid(&font, cellw, cellh, cpl, seq);
214         if(err)
215                 return 1;
216
217         if(invert)
218         {
219                 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
220                         font.image.data[i] = 255-font.image.data[i];
221         }
222         err = save_png(out_fn, &font.image, alpha);
223         if(err)
224                 return 1;
225
226         if(def_fn)
227                 save_defs(def_fn, &font);
228
229         for(i=0; (unsigned)i<font.n_glyphs; ++i)
230                 free(font.glyphs[i].image.data);
231         free(font.glyphs);
232         free(font.kerning);
233         free(font.image.data);
234
235         FT_Done_Face(face);
236         FT_Done_FreeType(freetype);
237
238         return 0;
239 }
240
241 void usage(void)
242 {
243         printf("ttf2png 1.1 - True Type Font to PNG converter\n"
244                 "Copyright (c) 2004-2018  Mikko Rasa, Mikkosoft Productions\n"
245                 "Distributed under the GNU General Public License\n\n");
246
247         printf("Usage: ttf2png [options] <TTF file>\n\n");
248
249         printf("Accepted options (default values in [brackets])\n"
250                 "  -r  Range of code points to convert [0,255]\n"
251                 "  -s  Font size to use, in pixels [10]\n"
252                 "  -l  Number of glyphs to put in one line [auto]\n"
253                 "  -c  Glyph cell size, in pixels (grid mode only) [auto]\n"
254                 "  -o  Output file name (or - for stdout) [font.png]\n");
255         printf("  -a  Force autohinter\n"
256                 "  -t  Render glyphs to alpha channel\n"
257                 "  -i  Invert colors of the glyphs\n"
258                 "  -v  Increase the level of verbosity\n"
259                 "  -e  Use cells in sequence, without gaps (grid mode only)\n"
260                 "  -p  Pack the glyphs tightly instead of in a grid\n"
261                 "  -m  Margin around image edges (packed mode only) [0]\n"
262                 "  -n  Padding between glyphs (packed mode only) [1]\n"
263                 "  -d  File name for writing glyph definitions\n"
264                 "  -h  Print this message\n");
265 }
266
267 int convert_numeric_option(char opt, int min_value)
268 {
269         int value;
270         char *ptr;
271
272         value = strtol(optarg, &ptr, 0);
273         if(value<min_value || *ptr)
274         {
275                 printf("Invalid option argument in -%c %s\n", opt, optarg);
276                 exit(1);
277         }
278
279         return value;
280 }
281
282 void convert_code_point_range(char opt, int *begin, int *end)
283 {
284         char *ptr;
285
286         if(!strcmp(optarg, "all"))
287         {
288                 *begin = 0;
289                 *end = 0x10FFFF;
290                 return;
291         }
292
293         *begin = strtol(optarg, &ptr, 0);
294         if(*begin>0 && *ptr==',')
295         {
296                 *end = strtol(ptr+1, &ptr, 0);
297                 if(*end>0 && !*ptr)
298                         return;
299         }
300
301         printf("Invalid option argument in -%c %s\n", opt, optarg);
302         exit(1);
303 }
304
305 void convert_size(char opt, int *width, int *height)
306 {
307         char *ptr;
308
309         if(!strcmp(optarg, "auto"))
310         {
311                 *width = 0;
312                 *height = 0;
313                 return;
314         }
315         else if(!strcmp(optarg, "autorect"))
316         {
317                 *width = 0;
318                 *height = 1;
319                 return;
320         }
321
322         *width = strtol(optarg, &ptr, 0);
323         if(*width>0)
324         {
325                 if(*ptr=='x')
326                 {
327                         *height = strtol(ptr+1, &ptr, 0);
328                         if(*height>0 && !*ptr)
329                                 return;
330                 }
331                 else if(!*ptr)
332                 {
333                         *height = *width;
334                         return;
335                 }
336         }
337
338         printf("Invalid option argument in -%c %s\n", opt, optarg);
339         exit(1);
340 }
341
342 unsigned round_to_pot(unsigned n)
343 {
344         n -= 1;
345         n |= n>>1;
346         n |= n>>2;
347         n |= n>>4;
348         n |= n>>8;
349         n |= n>>16;
350
351         return n+1;
352 }
353
354 void *alloc_image_data(size_t a, size_t b)
355 {
356         void *ptr;
357
358         /* Carry out the multiplication manually so we can check for overflow. */
359         while(b>1)
360         {
361                 size_t c = a;
362                 a *= 2;
363                 if(b&1)
364                         a += c;
365                 if(a<c)
366                 {
367                         fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
368                         return NULL;
369                 }
370                 b /= 2;
371         }
372         ptr = malloc(a);
373         if(!ptr)
374                 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
375         return ptr;
376 }
377
378 int init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
379 {
380         unsigned i, j;
381         unsigned size = 0;
382
383         font->ascent = (face->size->metrics.ascender+63)>>6;
384         font->descent = (face->size->metrics.descender+63)>>6;
385
386         if(verbose>=1)
387         {
388                 printf("Ascent:    %d\n", font->ascent);
389                 printf("Descent:   %d\n", font->descent);
390         }
391
392         font->n_glyphs = 0;
393         font->glyphs = NULL;
394         for(i=first; i<=last; ++i)
395         {
396                 unsigned n;
397                 FT_Bitmap *bmp = &face->glyph->bitmap;
398                 unsigned x, y;
399                 int flags = 0;
400                 Glyph *glyph;
401
402                 n = FT_Get_Char_Index(face, i);
403                 if(!n)
404                         continue;
405
406                 if(autohinter)
407                         flags |= FT_LOAD_FORCE_AUTOHINT;
408                 FT_Load_Glyph(face, n, flags);
409                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
410
411                 if(verbose>=2)
412                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
413
414                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
415                 {
416                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
417                         continue;
418                 }
419
420                 if(font->n_glyphs>=size)
421                 {
422                         size += 16;
423                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
424                 }
425
426                 glyph = &font->glyphs[font->n_glyphs++];
427                 glyph->index = n;
428                 glyph->code = i;
429                 glyph->image.w = bmp->width;
430                 glyph->image.h = bmp->rows;
431                 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
432                 if(!glyph->image.data)
433                 {
434                         fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
435                         return -1;
436                 }
437                 glyph->offset_x = face->glyph->bitmap_left;
438                 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
439                 glyph->advance = (int)(face->glyph->advance.x+32)/64;
440
441                 /* Copy the glyph image since FreeType uses a global buffer, which would
442                 be overwritten by the next glyph.  Negative pitch means the scanlines
443                 start from the bottom. */
444                 if(bmp->pitch<0)
445                 {
446                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
447                                 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
448                 }
449                 else
450                 {
451                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
452                                 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
453                 }
454         }
455
456         if(verbose>=1)
457                 printf("Loaded %u glyphs\n", font->n_glyphs);
458
459         size = 0;
460         font->n_kerning = 0;
461         font->kerning = NULL;
462         for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
463                 if(j!=i)
464                 {
465                         FT_Vector kerning;
466                         FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
467
468                         /* FreeType documentation says that vertical kerning is practically
469                         never used, so we ignore it. */
470                         if(kerning.x)
471                         {
472                                 Kerning *kern;
473
474                                 if(font->n_kerning>=size)
475                                 {
476                                         size += 16;
477                                         font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
478                                 }
479
480                                 kern = &font->kerning[font->n_kerning++];
481                                 kern->left_code = font->glyphs[i].code;
482                                 kern->right_code = font->glyphs[j].code;
483                                 kern->distance = kerning.x/64;
484                         }
485                 }
486
487         if(verbose>=1)
488                 printf("Loaded %d kerning pairs\n", font->n_kerning);
489
490         return 0;
491 }
492
493 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
494 {
495         unsigned i;
496         int top = 0, bot = 0;
497         unsigned first, last;
498         unsigned maxw = 0, maxh = 0;
499
500         /* Find extremes of the glyph images. */
501         for(i=0; i<font->n_glyphs; ++i)
502         {
503                 int y;
504
505                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
506                 if(y>top)
507                         top = y;
508                 if(font->glyphs[i].offset_y<bot)
509                         bot = font->glyphs[i].offset_y;
510                 if(font->glyphs[i].image.w>maxw)
511                         maxw = font->glyphs[i].image.w;
512                 if(font->glyphs[i].image.h>maxh)
513                         maxh = font->glyphs[i].image.h;
514         }
515
516         if(cellw==0)
517         {
518                 /* Establish a large enough cell to hold all glyphs in the range. */
519                 int square = (cellh==cellw);
520                 cellw = maxw;
521                 cellh = top-bot;
522                 if(square)
523                 {
524                         if(cellh>cellw)
525                                 cellw = cellh;
526                         else
527                                 cellh = cellw;
528                 }
529         }
530
531         if(verbose>=1)
532         {
533                 printf("Max size:  %u x %u\n", maxw, maxh);
534                 printf("Y range:   [%d %d]\n", bot, top);
535                 printf("Cell size: %u x %u\n", cellw, cellh);
536                 if(maxw>cellw || (unsigned)(top-bot)>cellh)
537                         fprintf(stderr, "Warning: character size exceeds cell size\n");
538         }
539
540         if(cpl==0)
541         {
542                 /* Determine number of characters per line, trying to fit all the glyphs
543                 in a square image. */
544                 for(i=1;; i<<=1)
545                 {
546                         cpl = i/cellw;
547                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
548                                 break;
549                 }
550         }
551
552         first = font->glyphs[0].code;
553         if(!seq)
554                 first -= first%cpl;
555         last = font->glyphs[font->n_glyphs-1].code;
556
557         font->image.w = round_to_pot(cpl*cellw);
558         if(seq)
559                 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
560         else
561                 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
562
563         font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
564         if(!font->image.data)
565                 return -1;
566         memset(font->image.data, 255, font->image.w*font->image.h);
567
568         for(i=0; i<font->n_glyphs; ++i)
569         {
570                 Glyph *glyph;
571                 unsigned ci, cx, cy;
572                 unsigned x, y;
573
574                 glyph = &font->glyphs[i];
575
576                 if(seq)
577                         ci = i;
578                 else
579                         ci = glyph->code-first;
580
581                 cx = (ci%cpl)*cellw;
582                 cy = (ci/cpl)*cellh;
583
584                 if(cellw>glyph->image.w)
585                         cx += (cellw-glyph->image.w)/2;
586                 cy += top-glyph->offset_y-glyph->image.h;
587
588                 glyph->x = cx;
589                 glyph->y = cy;
590
591                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
592                 {
593                         if(cx+x>=font->image.w || cy+y>=font->image.h)
594                                 continue;
595                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
596                 }
597         }
598
599         return 0;
600 }
601
602 int render_packed(Font *font, unsigned margin, unsigned padding)
603 {
604         unsigned i;
605         size_t area = 0;
606         char *used_glyphs;
607         unsigned *used_pixels;
608         unsigned cx = margin, cy;
609         unsigned used_h = 0;
610
611         /* Compute the total area occupied by glyphs and padding. */
612         for(i=0; i<font->n_glyphs; ++i)
613         {
614                 size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
615                 if(a<area)
616                 {
617                         fprintf(stderr, "Overflow in counting total glyph area\n");
618                         return -1;
619                 }
620                 area = a;
621         }
622
623         /* Find an image size that's no higher than wide, allowing for some
624         imperfections in the packing. */
625         for(font->image.w=1;; font->image.w<<=1)
626         {
627                 if(font->image.w<=margin*2)
628                         continue;
629                 font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
630                 if(font->image.h<=font->image.w)
631                         break;
632         }
633         font->image.h = round_to_pot(font->image.h);
634
635         /* Allocate arrays for storing the image and keeping track of used pixels and
636         glyphs.  Since glyphs are rectangular and the image is filled starting from
637         the top, it's enough to track the number of used pixels at the top of each
638         column. */
639         font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
640         if(!font->image.data)
641                 return -1;
642         memset(font->image.data, 255, font->image.w*font->image.h);
643         used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
644         memset(used_pixels, 0, font->image.w*sizeof(unsigned));
645         used_glyphs = (char *)malloc(font->n_glyphs);
646         memset(used_glyphs, 0, font->n_glyphs);
647
648         for(cy=margin; cy+margin<font->image.h;)
649         {
650                 unsigned w;
651                 unsigned x, y;
652                 Glyph *glyph = NULL;
653                 unsigned best_score = 0;
654                 unsigned target_h = 0;
655
656                 /* Find the leftmost free pixel on this row.  Also record the lowest
657                 extent of glyphs to the left of the free position. */
658                 for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
659                         if(used_pixels[cx]-cy-padding>target_h)
660                                 target_h = used_pixels[cx]-cy-padding;
661
662                 if(cx+margin>=font->image.w)
663                 {
664                         cx = margin;
665                         ++cy;
666                         continue;
667                 }
668
669                 /* Count the free pixel at this position. */
670                 for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
671
672                 /* Find a suitable glyph to put here. */
673                 for(i=0; i<font->n_glyphs; ++i)
674                 {
675                         Glyph *g;
676
677                         g = &font->glyphs[i];
678                         if(!used_glyphs[i] && g->image.w<=w)
679                         {
680                                 unsigned score;
681
682                                 /* Prefer glyphs that would reach exactly as low as the ones left
683                                 of here.  This aims to create a straight edge at the bottom for
684                                 lining up further glyphs. */
685                                 score = g->image.h+padding;
686                                 if(g->image.h==target_h)
687                                         score *= g->image.w;
688                                 else
689                                         score += g->image.w;
690
691                                 if(score>best_score)
692                                 {
693                                         glyph = g;
694                                         best_score = score;
695                                 }
696                         }
697                 }
698
699                 if(!glyph)
700                 {
701                         cx += w;
702                         continue;
703                 }
704
705                 used_glyphs[glyph-font->glyphs] = 1;
706                 glyph->x = cx;
707                 glyph->y = cy;
708
709                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
710                 {
711                         if(cx+x>=font->image.w || cy+y>=font->image.h)
712                                 continue;
713                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
714                 }
715                 for(x=0; x<glyph->image.w+2*padding; ++x)
716                 {
717                         if(cx+x<padding || cx+x>=font->image.w+padding)
718                                 continue;
719                         if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
720                                 used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
721                 }
722
723                 if(cy+glyph->image.h+margin>used_h)
724                         used_h = cy+glyph->image.h+margin;
725         }
726
727         /* Trim the image to the actually used size, in case the original estimate
728         was too pessimistic. */
729         font->image.h = round_to_pot(used_h);
730
731         free(used_glyphs);
732         free(used_pixels);
733
734         return 0;
735 }
736
737 int save_defs(const char *fn, const Font *font)
738 {
739         FILE *out;
740         unsigned i;
741
742         out = fopen(fn, "w");
743         if(!out)
744         {
745                 fprintf(stderr, "Couldn't open %s\n",fn);
746                 return -1;
747         }
748
749         fprintf(out, "# Image/font info:\n");
750         fprintf(out, "# width height size ascent descent\n");
751         fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
752         fprintf(out, "\n# Glyph info:\n");
753         fprintf(out, "# code x y width height offset_x offset_y advance\n");
754         for(i=0; i<font->n_glyphs; ++i)
755         {
756                 const Glyph *g = &font->glyphs[i];
757                 fprintf(out, "glyph %u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance);
758         }
759         fprintf(out, "\n# Kerning info:\n");
760         fprintf(out, "# left right distance\n");
761         for(i=0; i<font->n_kerning; ++i)
762         {
763                 const Kerning *k = &font->kerning[i];
764                 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
765         }
766
767         fclose(out);
768
769         return 0;
770 }
771
772 int save_png(const char *fn, const Image *image, char alpha)
773 {
774         FILE *out;
775         png_struct *pngs;
776         png_info *pngi;
777         png_byte **rows;
778         unsigned i;
779         png_byte *data2 = 0;
780         int color;
781
782         if(!strcmp(fn, "-"))
783                 out = stdout;
784         else
785         {
786                 out = fopen(fn, "wb");
787                 if(!out)
788                 {
789                         fprintf(stderr, "Couldn't open %s\n",fn);
790                         return -1;
791                 }
792         }
793
794         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
795         if(!pngs)
796         {
797                 fprintf(stderr, "Error writing PNG file\n");
798                 return -1;
799         }
800         pngi = png_create_info_struct(pngs);
801         if(!pngi)
802         {
803                 png_destroy_write_struct(&pngs, NULL);
804                 fprintf(stderr, "Error writing PNG file\n");
805                 return -1;
806         }
807
808         png_init_io(pngs, out);
809         rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
810         if(alpha)
811         {
812                 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
813                 if(!data2)
814                         return -1;
815                 for(i=0; i<image->w*image->h; ++i)
816                 {
817                         data2[i*2] = 255;
818                         data2[i*2+1] = 255-image->data[i];
819                 }
820                 for(i=0; i<image->h; ++i)
821                         rows[i] = (png_byte *)(data2+i*image->w*2);
822                 color = PNG_COLOR_TYPE_GRAY_ALPHA;
823         }
824         else
825         {
826                 for(i=0; i<image->h; ++i)
827                         rows[i] = (png_byte *)(image->data+i*image->w);
828                 color = PNG_COLOR_TYPE_GRAY;
829         }
830         png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
831         png_set_rows(pngs, pngi, rows);
832         png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
833         png_destroy_write_struct(&pngs, &pngi);
834         free(rows);
835         if(alpha)
836                 free(data2);
837
838         if(verbose)
839                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
840
841         fclose(out);
842
843         return 0;
844 }