]> git.tdb.fi Git - ttf2png.git/commitdiff
Add support for generating distance field textures
authorMikko Rasa <tdb@tdb.fi>
Thu, 3 May 2018 16:24:59 +0000 (19:24 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 3 May 2018 16:24:59 +0000 (19:24 +0300)
Readme
ttf2png.c

diff --git a/Readme b/Readme
index 946c9b72f9ff8d93168995f3607e520f409db44a..7695e617997943e4f235f74d71913a776cfd2a69 100644 (file)
--- a/Readme
+++ b/Readme
@@ -69,10 +69,17 @@ Command-line options
     Control the amount of padding between glyphs.  The default is 1 pixel.
     Only used with -p.
 
-  -f
+  -g
     Allow the resulting image to have a non-power-of-two size.  By default the
     image size is rounded up to a power of two for maximum compatibility.
 
+  -f <num>
+    Generate a distance field texture.  The argument controls the amount of
+    oversampling when calculating distances.  Larger values may produce more
+    accurate results but take longer to process.  Distance fields are always
+    stored without alpha and with large/positive values indicating the inside
+    of glyphs; the -t and -i options are ignored.
+
   -d
     File name to write glyph definitions.  See the section below for details.
 
@@ -144,6 +151,7 @@ next
 - Alternate ways of specifying code point ranges
 - Multiple code point ranges can be specified
 - Option to generate non-power-of-two images
+- Support for distance field generation
 
 1.1
 - Controllable margin and padding in packed mode
index e890e33643084f7784c8d23e280555c425e8fec4..a634b2b9def92998add21bd3e7ecfebf077db243 100644 (file)
--- a/ttf2png.c
+++ b/ttf2png.c
@@ -78,9 +78,11 @@ 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);
-int init_glyphs(Font *, FT_Face, const Range *, bool);
+int init_font(Font *, FT_Face, const Range *, unsigned, bool, unsigned);
+int init_glyphs(Font *, FT_Face, const Range *, bool, unsigned);
 int copy_bitmap(const FT_Bitmap *, Image *);
+void propagate_distance(unsigned short *, int);
+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 save_defs(const char *, const Font *);
@@ -105,6 +107,7 @@ int main(int argc, char **argv)
        unsigned margin = 0;
        unsigned padding = 1;
        bool npot = 0;
+       unsigned distfield = 0;
 
        FT_Library freetype;
        FT_Face face;
@@ -123,7 +126,7 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:f")) != -1)
+       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:gf:")) != -1)
        {
                switch(i)
                {
@@ -174,9 +177,12 @@ int main(int argc, char **argv)
                case 'n':
                        padding = convert_numeric_option('n', 0);
                        break;
-               case 'f':
+               case 'g':
                        npot = 1;
                        break;
+               case 'f':
+                       distfield = convert_numeric_option('f', 1);
+                       break;
                }
        }
        if(!strcmp(out_fn, "-"))
@@ -213,7 +219,7 @@ int main(int argc, char **argv)
                printf("Glyphs:    %ld\n", face->num_glyphs);
        }
 
-       err = FT_Set_Pixel_Sizes(face, 0, size);
+       err = FT_Set_Pixel_Sizes(face, 0, (distfield ? size*distfield : size));
        if(err)
        {
                fprintf(stderr, "Couldn't set size\n");
@@ -231,7 +237,7 @@ int main(int argc, char **argv)
                sort_and_compact_ranges(ranges, &n_ranges);
 
        font.size = size;
-       err = init_font(&font, face, ranges, n_ranges, autohinter);
+       err = init_font(&font, face, ranges, n_ranges, autohinter, distfield);
        if(err)
                return 1;
 
@@ -248,12 +254,12 @@ int main(int argc, char **argv)
        if(err)
                return 1;
 
-       if(invert)
+       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);
+       err = save_png(out_fn, &font.image, (alpha && !distfield));
        if(err)
                return 1;
 
@@ -295,7 +301,8 @@ void usage(void)
                "  -p  Pack the glyphs tightly instead of in a grid\n"
                "  -m  Margin around image edges (packed mode only) [0]\n"
                "  -n  Padding between glyphs (packed mode only) [1]\n"
-               "  -f  Allow non-power-of-two result\n"
+               "  -g  Allow non-power-of-two result\n"
+               "  -f  Create a distance field texture\n"
                "  -d  File name for writing glyph definitions\n"
                "  -h  Print this message\n");
 }
@@ -497,13 +504,14 @@ void *alloc_image_data(size_t a, size_t b)
        return ptr;
 }
 
-int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter)
+int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter, unsigned distfield)
 {
        unsigned i, j;
        unsigned size = 0;
+       int scale = (distfield>0 ? distfield : 1);
 
-       font->ascent = (face->size->metrics.ascender+63)/64;
-       font->descent = (face->size->metrics.descender-63)/64;
+       font->ascent = (face->size->metrics.ascender/scale+63)/64;
+       font->descent = (face->size->metrics.descender/scale-63)/64;
 
        if(verbose>=1)
        {
@@ -514,7 +522,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))
+               if(init_glyphs(font, face, &ranges[i], autohinter, distfield))
                        return -1;
 
        if(verbose>=1)
@@ -543,7 +551,7 @@ int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges,
                                kern = &font->kerning[font->n_kerning++];
                                kern->left_code = font->glyphs[i].code;
                                kern->right_code = font->glyphs[j].code;
-                               kern->distance = (kerning.x+32)/64;
+                               kern->distance = (kerning.x/scale+32)/64;
                        }
                }
 
@@ -553,10 +561,11 @@ 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)
+int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, unsigned distfield)
 {
        unsigned i, j;
        unsigned size = font->n_glyphs;
+       int scale = (distfield>0 ? distfield : 1);
 
        for(i=range->first; i<=range->last; ++i)
        {
@@ -572,7 +581,7 @@ int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
                if(autohinter)
                        flags |= FT_LOAD_FORCE_AUTOHINT;
                FT_Load_Glyph(face, n, flags);
-               FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
+               FT_Render_Glyph(face->glyph, (distfield ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL));
 
                if(verbose>=2)
                {
@@ -592,7 +601,7 @@ int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
 
                                printf(" (%s)", utf8);
                        }
-                       printf(": glyph %u, size %dx%d\n", n, bmp->width, bmp->rows);
+                       printf(": glyph %u, size %dx%d\n", n, bmp->width/scale, bmp->rows/scale);
                }
 
                if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY && bmp->pixel_mode!=FT_PIXEL_MODE_MONO)
@@ -610,15 +619,26 @@ int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
                glyph = &font->glyphs[font->n_glyphs++];
                glyph->index = n;
                glyph->code = i;
-               glyph->offset_x = face->glyph->bitmap_left;
-               glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
-               glyph->advance = (int)(face->glyph->advance.x+32)/64;
+               glyph->offset_x = (int)(face->glyph->bitmap_left+scale/2)/scale;
+               glyph->offset_y = (int)(face->glyph->bitmap_top-bmp->rows+scale/2)/scale;
+               glyph->advance = (int)(face->glyph->advance.x/scale+32)/64;
 
                /* Copy the glyph image since FreeType uses a global buffer, which would
                be overwritten by the next glyph.  Negative pitch means the scanlines
                start from the bottom. */
-               if(copy_bitmap(bmp, &glyph->image))
-                       return -1;
+               if(distfield)
+               {
+                       unsigned margin = 3;
+
+                       glyph->offset_x -= margin;
+                       glyph->offset_y -= margin;
+                       create_distance_field(bmp, &glyph->image, distfield, margin);
+               }
+               else
+               {
+                       if(copy_bitmap(bmp, &glyph->image))
+                               return -1;
+               }
        }
 
        return 0;
@@ -671,6 +691,100 @@ int copy_bitmap(const FT_Bitmap *bmp, Image *image)
        return 0;
 }
 
+void propagate_distance(unsigned short *pixel, int offset)
+{
+       unsigned short *neighbor = pixel+offset;
+       if((*neighbor^*pixel)&0x8000)
+               *neighbor = (*neighbor&0x8000)+1;
+       else if((*neighbor&0x7FFF)>(*pixel&0x7FFF))
+               *neighbor = *pixel+2;
+}
+
+int create_distance_field(const FT_Bitmap *bmp, Image *image, unsigned scale, unsigned margin)
+{
+       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;
+       }
+
+       if(copy_bitmap(bmp, &base_image))
+               return -1;
+
+       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)
+       {
+               fprintf(stderr, "Cannot allocate %d bytes of memory for distance map\n", map_w*map_h);
+               free(base_image.data);
+               return -1;
+       }
+
+       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(x=0; x<map_w*map_h; ++x)
+               distance_map[x] = 0x7FFF;
+
+       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;
+
+       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);
+       }
+
+       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);
+       }
+
+       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;
+       }
+
+       free(distance_map);
+       free(base_image.data);
+
+       return 0;
+}
+
 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq, bool npot)
 {
        unsigned i;