From c7b77aeb1a478f53c47d2aa08e7cc7728aa0898b Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 3 May 2018 19:24:59 +0300 Subject: [PATCH] Add support for generating distance field textures --- Readme | 10 +++- ttf2png.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 145 insertions(+), 23 deletions(-) diff --git a/Readme b/Readme index 946c9b7..7695e61 100644 --- 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 + 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 diff --git a/ttf2png.c b/ttf2png.c index e890e33..a634b2b 100644 --- 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)i0 ? 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=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; x0; --x) + propagate_distance(distance_map+x+y*map_w, -1); + } + + for(x=0; x0; --y) + propagate_distance(distance_map+x+y*map_w, -map_w); + } + + offset = scale/2*(map_w+1); + for(y=0; yh; ++y) for(x=0; xw; ++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; -- 2.45.2