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