]> git.tdb.fi Git - ttf2png.git/blobdiff - ttf2png.c
Refactor image initialization
[ttf2png.git] / ttf2png.c
index a634b2b9def92998add21bd3e7ecfebf077db243..93576c1eb4daec90ec7db69f8ad3efac83060302 100644 (file)
--- a/ttf2png.c
+++ b/ttf2png.c
@@ -1,6 +1,6 @@
 /*
 ttf2png - True Type Font to PNG converter
-Copyright (c) 2004-2008 Mikko Rasa
+Copyright (c) 2004-2018 Mikko Rasa, Mikkosoft Productions
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -77,16 +77,17 @@ void convert_size(char, unsigned *, unsigned *);
 void sort_and_compact_ranges(Range *, unsigned *);
 int range_cmp(const void *, const void *);
 unsigned round_to_pot(unsigned);
-void *alloc_image_data(size_t, size_t);
-int init_font(Font *, FT_Face, const Range *, unsigned, bool, unsigned);
-int init_glyphs(Font *, FT_Face, const Range *, bool, unsigned);
+int init_image(Image *, size_t, size_t);
+int init_font(Font *, FT_Face, const Range *, unsigned, bool, unsigned, unsigned);
+int init_glyphs(Font *, FT_Face, const Range *, bool, unsigned, unsigned);
 int copy_bitmap(const FT_Bitmap *, Image *);
-void propagate_distance(unsigned short *, int);
+unsigned sqrti(unsigned);
+unsigned find_distance_to_edge(const Image *, int, int, unsigned);
 int create_distance_field(const FT_Bitmap *, Image *, unsigned, unsigned);
-int render_grid(Font *, unsigned, unsigned, unsigned, bool, bool);
-int render_packed(Font *, unsigned, unsigned, bool);
+int render_grid(Font *, unsigned, unsigned, unsigned, bool);
+int render_packed(Font *, unsigned, unsigned);
 int save_defs(const char *, const Font *);
-int save_png(const char *, const Image *, char);
+int save_png(const char *, const Image *, bool, bool, bool);
 
 char verbose = 0;
 
@@ -108,6 +109,7 @@ int main(int argc, char **argv)
        unsigned padding = 1;
        bool npot = 0;
        unsigned distfield = 0;
+       unsigned border = 0;
 
        FT_Library freetype;
        FT_Face face;
@@ -126,7 +128,7 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:gf:")) != -1)
+       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:gf:b:")) != -1)
        {
                switch(i)
                {
@@ -183,6 +185,9 @@ int main(int argc, char **argv)
                case 'f':
                        distfield = convert_numeric_option('f', 1);
                        break;
+               case 'b':
+                       border = convert_numeric_option('b', 1);
+                       break;
                }
        }
        if(!strcmp(out_fn, "-"))
@@ -219,7 +224,15 @@ int main(int argc, char **argv)
                printf("Glyphs:    %ld\n", face->num_glyphs);
        }
 
-       err = FT_Set_Pixel_Sizes(face, 0, (distfield ? size*distfield : size));
+       font.size = size;
+       if(distfield)
+       {
+               if(!border)
+                       border = sqrti(font.size);
+               size *= distfield;
+       }
+
+       err = FT_Set_Pixel_Sizes(face, 0, size);
        if(err)
        {
                fprintf(stderr, "Couldn't set size\n");
@@ -236,8 +249,7 @@ int main(int argc, char **argv)
        else
                sort_and_compact_ranges(ranges, &n_ranges);
 
-       font.size = size;
-       err = init_font(&font, face, ranges, n_ranges, autohinter, distfield);
+       err = init_font(&font, face, ranges, n_ranges, autohinter, distfield, border);
        if(err)
                return 1;
 
@@ -248,18 +260,13 @@ int main(int argc, char **argv)
        }
 
        if(pack)
-               err = render_packed(&font, margin, padding, npot);
+               err = render_packed(&font, margin, padding);
        else
-               err = render_grid(&font, cellw, cellh, cpl, seq, npot);
+               err = render_grid(&font, cellw, cellh, cpl, seq);
        if(err)
                return 1;
 
-       if(invert || distfield)
-       {
-               for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
-                       font.image.data[i] = 255-font.image.data[i];
-       }
-       err = save_png(out_fn, &font.image, (alpha && !distfield));
+       err = save_png(out_fn, &font.image, (alpha && !distfield), (invert || distfield), npot);
        if(err)
                return 1;
 
@@ -480,31 +487,35 @@ unsigned round_to_pot(unsigned n)
        return n+1;
 }
 
-void *alloc_image_data(size_t a, size_t b)
+int init_image(Image *image, size_t w, size_t h)
 {
-       void *ptr;
+       size_t s;
+
+       image->w = w;
+       image->h = h;
+       image->data = NULL;
+
+       if(!image->w || !image->h)
+               return 0;
 
-       /* Carry out the multiplication manually so we can check for overflow. */
-       while(b>1)
+       s = w*h;
+       if(s/h!=w)
        {
-               size_t c = a;
-               a *= 2;
-               if(b&1)
-                       a += c;
-               if(a<c)
-               {
-                       fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
-                       return NULL;
-               }
-               b /= 2;
+               fprintf(stderr, "Cannot allocate memory for a %dx%d image\n", image->w, image->h);
+               return -1;
        }
-       ptr = malloc(a);
-       if(!ptr)
-               fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
-       return ptr;
+
+       image->data = malloc(s);
+       if(!image->data)
+       {
+               fprintf(stderr, "Cannot allocate memory for a %dx%d image\n", image->w, image->h);
+               return -1;
+       }
+
+       return 0;
 }
 
-int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter, unsigned distfield)
+int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter, unsigned distfield, unsigned border)
 {
        unsigned i, j;
        unsigned size = 0;
@@ -522,7 +533,7 @@ int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges,
        font->n_glyphs = 0;
        font->glyphs = NULL;
        for(i=0; i<n_ranges; ++i)
-               if(init_glyphs(font, face, &ranges[i], autohinter, distfield))
+               if(init_glyphs(font, face, &ranges[i], autohinter, distfield, border))
                        return -1;
 
        if(verbose>=1)
@@ -561,7 +572,7 @@ int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges,
        return 0;
 }
 
-int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, unsigned distfield)
+int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, unsigned distfield, unsigned border)
 {
        unsigned i, j;
        unsigned size = font->n_glyphs;
@@ -628,11 +639,9 @@ int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, u
                start from the bottom. */
                if(distfield)
                {
-                       unsigned margin = 3;
-
-                       glyph->offset_x -= margin;
-                       glyph->offset_y -= margin;
-                       create_distance_field(bmp, &glyph->image, distfield, margin);
+                       glyph->offset_x -= border;
+                       glyph->offset_y -= border;
+                       create_distance_field(bmp, &glyph->image, distfield, border);
                }
                else
                {
@@ -650,20 +659,10 @@ int copy_bitmap(const FT_Bitmap *bmp, Image *image)
        unsigned char *src;
        unsigned char *dst;
 
-       image->w = bmp->width;
-       image->h = bmp->rows;
+       if(init_image(image, bmp->width, bmp->rows))
+               return -1;
        if(!image->w || !image->h)
-       {
-               image->data = NULL;
                return 0;
-       }
-
-       image->data = (unsigned char *)malloc(image->w*image->h);
-       if(!image->data)
-       {
-               fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", image->w*image->h);
-               return -1;
-       }
 
        if(bmp->pitch<0)
                src = bmp->buffer+(bmp->rows-1)*-bmp->pitch;
@@ -691,105 +690,86 @@ int copy_bitmap(const FT_Bitmap *bmp, Image *image)
        return 0;
 }
 
-void propagate_distance(unsigned short *pixel, int offset)
+unsigned sqrti(unsigned num)
 {
-       unsigned short *neighbor = pixel+offset;
-       if((*neighbor^*pixel)&0x8000)
-               *neighbor = (*neighbor&0x8000)+1;
-       else if((*neighbor&0x7FFF)>(*pixel&0x7FFF))
-               *neighbor = *pixel+2;
+       unsigned result = (num>0xFFFF ? 0xFFFF : 0x100);
+       while(result && result*result>=result+num)
+               result -= (result*result+result-num)/(result*2);
+
+       return result;
 }
 
-int create_distance_field(const FT_Bitmap *bmp, Image *image, unsigned scale, unsigned margin)
+unsigned find_distance_to_edge(const Image *image, int origin_x, int origin_y, unsigned range)
 {
-       unsigned x, y;
-       Image base_image;
-       unsigned short *distance_map;
-       unsigned map_w;
-       unsigned map_h;
-       unsigned offset;
-
-       if(!bmp->width || !bmp->rows)
-       {
-               image->w = 0;
-               image->h = 0;
-               image->data = NULL;
-               return 0;
-       }
+       unsigned i, j;
+       int x, y;
+       unsigned char origin_pixel = 0;
+       unsigned closest = range*range;
 
-       if(copy_bitmap(bmp, &base_image))
-               return -1;
+       if(origin_x>=0 && (unsigned)origin_x<image->w && origin_y>=0 && (unsigned)origin_y<image->h)
+               origin_pixel = image->data[origin_x+origin_y*image->w];
 
-       map_w = base_image.w+2*margin*scale+scale-1;
-       map_w -= map_w%scale;
-       map_h = base_image.h+2*margin*scale+scale-1;
-       map_h -= map_h%scale;
-       distance_map = (unsigned short *)malloc(map_w*map_h*sizeof(unsigned short));
-       if(!distance_map)
+       x = origin_x-1;
+       y = origin_y-1;
+       for(i=1; (i<range && i*i<=closest); ++i, --x, --y) for(j=0; j<4; ++j)
        {
-               fprintf(stderr, "Cannot allocate %d bytes of memory for distance map\n", map_w*map_h);
-               free(base_image.data);
-               return -1;
-       }
+               unsigned k;
+               int dx = (j==0 ? 1 : j==2 ? -1 : 0);
+               int dy = (j==1 ? 1 : j==3 ? -1 : 0);
 
-       image->w = map_w/scale;
-       image->h = map_h/scale;
-       image->data = (unsigned char *)malloc(image->w*image->h);
-       if(!image->data)
-       {
-               fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", image->w*image->h);
-               free(base_image.data);
-               free(distance_map);
-               return -1;
+               for(k=0; k<i*2; ++k, x+=dx, y+=dy)
+               {
+                       unsigned char pixel = 0;
+                       if(x>=0 && (unsigned)x<image->w && y>=0 && (unsigned)y<image->h)
+                               pixel = image->data[x+y*image->w];
+                               
+                       if((pixel^origin_pixel)&0x80)
+                       {
+                               unsigned d = 2*i*i + k*k - 2*k*i;
+                               if(d<closest)
+                                       closest = d;
+                       }
+               }
        }
 
-       for(x=0; x<map_w*map_h; ++x)
-               distance_map[x] = 0x7FFF;
+       return sqrti(closest*0x3F01)/range;
+}
 
-       offset = margin*scale*(map_w+1);
-       for(y=0; y<base_image.h; ++y) for(x=0; x<base_image.w; ++x)
-               distance_map[offset+x+y*map_w] |= (base_image.data[x+y*base_image.w]&0x80)<<8;
+int create_distance_field(const FT_Bitmap *bmp, Image *image, unsigned scale, unsigned margin)
+{
+       unsigned x, y;
+       Image base_image;
 
-       for(y=0; y<map_h; ++y)
-       {
-               for(x=0; x+1<map_w; ++x)
-                       propagate_distance(distance_map+x+y*map_w, 1);
-               for(x=map_w-1; x>0; --x)
-                       propagate_distance(distance_map+x+y*map_w, -1);
-       }
+       if(init_image(image, (bmp->width+scale-1)/scale+2*margin, (bmp->rows+scale-1)/scale+2*margin))
+               return -1;
+       if(!image->w || !image->h)
+               return 0;
 
-       for(x=0; x<map_w; ++x)
-       {
-               for(y=0; y+1<map_h; ++y)
-                       propagate_distance(distance_map+x+y*map_w, map_w);
-               for(y=map_h-1; y>0; --y)
-                       propagate_distance(distance_map+x+y*map_w, -map_w);
-       }
+       if(copy_bitmap(bmp, &base_image))
+               return -1;
 
-       offset = scale/2*(map_w+1);
        for(y=0; y<image->h; ++y) for(x=0; x<image->w; ++x)
        {
-               unsigned short pixel = distance_map[offset+(x+y*map_w)*scale];
-               unsigned short dist = (pixel&0x7FFF)*0x7F/(margin*scale*2+1);
-               if(dist>0x7F)
-                       dist = 0x7F;
-               if(pixel&0x8000)
-                       image->data[x+y*image->w] = 0x80+dist;
-               else
-                       image->data[x+y*image->w] = 0x7F-dist;
+               int bx = (x-margin)*scale+scale/2;
+               int by = (y-margin)*scale+scale/2;
+               unsigned char pixel = find_distance_to_edge(&base_image, bx, by, margin*scale);
+               if(bx>=0 && (unsigned)bx<base_image.w && by>=0 && (unsigned)by<base_image.h)
+                       pixel |= base_image.data[bx+by*base_image.w]&0x80;
+               if(!(pixel&0x80))
+                       pixel = 0x80-pixel;
+               image->data[x+y*image->w] = pixel;
        }
 
-       free(distance_map);
        free(base_image.data);
 
        return 0;
 }
 
-int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq, bool npot)
+int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq)
 {
        unsigned i;
        int top = 0, bot = 0;
-       unsigned first, last;
+       unsigned first, n_cells;
        unsigned maxw = 0, maxh = 0;
 
        /* Find extremes of the glyph images. */
@@ -845,24 +825,17 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s
        }
 
        first = font->glyphs[0].code;
-       if(!seq)
-               first -= first%cpl;
-       last = font->glyphs[font->n_glyphs-1].code;
-
-       font->image.w = cpl*cellw;
-       if(!npot)
-               font->image.w = round_to_pot(font->image.w);
        if(seq)
-               font->image.h = (font->n_glyphs+cpl-1)/cpl*cellh;
+               n_cells = font->n_glyphs;
        else
-               font->image.h = (last-first+cpl)/cpl*cellh;
-       if(!npot)
-               font->image.h = round_to_pot(font->image.h);
+       {
+               first -= first%cpl;
+               n_cells = font->glyphs[font->n_glyphs-1].code+1-first;
+       }
 
-       font->image.data = (unsigned char *)alloc_image_data(font->image.w, font->image.h);
-       if(!font->image.data)
+       if(init_image(&font->image, cpl*cellw, (n_cells+cpl-1)/cpl*cellh))
                return -1;
-       memset(font->image.data, 255, font->image.w*font->image.h);
+       memset(font->image.data, 0, font->image.w*font->image.h);
 
        for(i=0; i<font->n_glyphs; ++i)
        {
@@ -891,14 +864,14 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s
                {
                        if(cx+x>=font->image.w || cy+y>=font->image.h)
                                continue;
-                       font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
+                       font->image.data[cx+x+(cy+y)*font->image.w] = glyph->image.data[x+y*glyph->image.w];
                }
        }
 
        return 0;
 }
 
-int render_packed(Font *font, unsigned margin, unsigned padding, bool npot)
+int render_packed(Font *font, unsigned margin, unsigned padding)
 {
        unsigned i;
        size_t area = 0;
@@ -919,27 +892,26 @@ int render_packed(Font *font, unsigned margin, unsigned padding, bool npot)
                area = a;
        }
 
-       /* Find an image size that's no higher than wide, allowing for some
-       imperfections in the packing. */
+       /* Find an image size that's approximately square. */
        for(font->image.w=1;; font->image.w<<=1)
        {
                if(font->image.w<=margin*2)
                        continue;
-               font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
+               font->image.h = area/(font->image.w-margin*2)+margin*2;
                if(font->image.h<=font->image.w)
                        break;
        }
-       if(!npot)
-               font->image.h = round_to_pot(font->image.h);
+
+       /* Add some extra space to accommodate packing imperfections. */
+       font->image.h = font->image.h*3/2;
 
        /* Allocate arrays for storing the image and keeping track of used pixels and
        glyphs.  Since glyphs are rectangular and the image is filled starting from
        the top, it's enough to track the number of used pixels at the top of each
        column. */
-       font->image.data = (unsigned char *)alloc_image_data(font->image.w, font->image.h);
-       if(!font->image.data)
+       if(init_image(&font->image, font->image.w, font->image.h))
                return -1;
-       memset(font->image.data, 255, font->image.w*font->image.h);
+       memset(font->image.data, 0, font->image.w*font->image.h);
        used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
        memset(used_pixels, 0, font->image.w*sizeof(unsigned));
        used_glyphs = (char *)malloc(font->n_glyphs);
@@ -1010,7 +982,7 @@ int render_packed(Font *font, unsigned margin, unsigned padding, bool npot)
                {
                        if(cx+x>=font->image.w || cy+y>=font->image.h)
                                continue;
-                       font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
+                       font->image.data[cx+x+(cy+y)*font->image.w] = glyph->image.data[x+y*glyph->image.w];
                }
                for(x=0; x<glyph->image.w+2*padding; ++x)
                {
@@ -1027,8 +999,6 @@ int render_packed(Font *font, unsigned margin, unsigned padding, bool npot)
        /* Trim the image to the actually used size, in case the original estimate
        was too pessimistic. */
        font->image.h = used_h;
-       if(!npot)
-               font->image.h = round_to_pot(font->image.h);
 
        free(used_glyphs);
        free(used_pixels);
@@ -1071,15 +1041,17 @@ int save_defs(const char *fn, const Font *font)
        return 0;
 }
 
-int save_png(const char *fn, const Image *image, char alpha)
+int save_png(const char *fn, const Image *image, bool alpha, bool invert, bool npot)
 {
        FILE *out;
        png_struct *pngs;
        png_info *pngi;
-       png_byte **rows;
-       unsigned i;
-       png_byte *data2 = 0;
+       unsigned w, h;
+       png_byte *row;
+       unsigned x, y;
        int color;
+       unsigned flip_bits = (invert==alpha ? 0xFF : 0x00);
+       unsigned char *src = image->data;
 
        if(!strcmp(fn, "-"))
                out = stdout;
@@ -1107,38 +1079,53 @@ int save_png(const char *fn, const Image *image, char alpha)
                return -1;
        }
 
+       w = (npot ? image->w : round_to_pot(image->w));
+       h = (npot ? image->h : round_to_pot(image->h));
+       color = (alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY);
+       png_set_IHDR(pngs, pngi, w, h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
        png_init_io(pngs, out);
-       rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
+       png_write_info(pngs, pngi);
+       row = (png_byte *)malloc(w*(1+alpha));
        if(alpha)
        {
-               data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
-               if(!data2)
-                       return -1;
-               for(i=0; i<image->w*image->h; ++i)
+               for(x=0; x<w; ++x)
+               {
+                       row[x*2] = 255;
+                       row[x*2+1] = flip_bits;
+               }
+               for(y=0; y<image->h; ++y)
                {
-                       data2[i*2] = 255;
-                       data2[i*2+1] = 255-image->data[i];
+                       for(x=0; x<image->w; ++x)
+                               row[x*2+1] = *src++^flip_bits;
+                       png_write_row(pngs, row);
                }
-               for(i=0; i<image->h; ++i)
-                       rows[i] = (png_byte *)(data2+i*image->w*2);
-               color = PNG_COLOR_TYPE_GRAY_ALPHA;
+
+               for(x=0; x<w; ++x)
+                       row[x*2+1] = 0;
        }
        else
        {
-               for(i=0; i<image->h; ++i)
-                       rows[i] = (png_byte *)(image->data+i*image->w);
-               color = PNG_COLOR_TYPE_GRAY;
+               memset(row+image->w, flip_bits, w-image->w);
+               for(y=0; y<image->h; ++y)
+               {
+                       for(x=0; x<image->w; ++x)
+                               row[x] = *src++^flip_bits;
+                       png_write_row(pngs, row);
+               }
+
+               memset(row, flip_bits, w);
        }
-       png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-       png_set_rows(pngs, pngi, rows);
-       png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
+
+       for(; y<h; ++y)
+               png_write_row(pngs, row);
+
+       png_write_end(pngs, pngi);
        png_destroy_write_struct(&pngs, &pngi);
-       free(rows);
-       if(alpha)
-               free(data2);
+       free(row);
 
        if(verbose)
-               printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
+               printf("Saved %dx%d PNG image to %s\n", w, h, fn);
 
        fclose(out);