From 6216f2109e6cd59dd81dd3a5df331d2209aca2bc Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 2 Nov 2008 22:41:25 +0200 Subject: [PATCH] Version 0.3 --- Makefile | 2 +- Readme | 7 +- ttf2png.c | 493 +++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 351 insertions(+), 151 deletions(-) diff --git a/Makefile b/Makefile index 7ec1398..6219108 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ ttf2png: ttf2png.c gcc -Wall $^ -o $@ $(shell freetype-config --cflags --libs) $(shell pkg-config --cflags --libs libpng12) -VER=0.2.1 +VER=0.3 .PHONY: tarball tarball: ttf2png-$(VER).tar.gz diff --git a/Readme b/Readme index c6b8100..ff85428 100644 --- a/Readme +++ b/Readme @@ -1,5 +1,5 @@ ttf2png - True Type Font to PNG converter -Copyright (c) 2004-2007 Mikko Rasa, Mikkosoft Productions +Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions Software requirements @@ -11,6 +11,11 @@ C compiler (preferably GCC) Changes +0.3 +- Restructure the code +- Add tight packing mode +- Autodetect cell size and chars-per-line in grid mode + 0.2.2 - Write both X and Y offsets of glyphs to the definition file - Output font ascent and descent to definition file diff --git a/ttf2png.c b/ttf2png.c index fe785e0..b499b43 100644 --- a/ttf2png.c +++ b/ttf2png.c @@ -1,6 +1,6 @@ /* ttf2png - True Type Font to PNG converter -Copyright (c) 2004-2007 Mikko Rasa +Copyright (c) 2004-2008 Mikko Rasa 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 @@ -18,26 +18,46 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include #include #include FT_FREETYPE_H -typedef struct sGlyphDef +typedef struct sImage { - int code; - int x,y; - int w,h; - int offset_x; - int offset_y; - int advance; -} GlyphDef; + unsigned w, h; + char *data; +} Image; + +typedef struct sGlyph +{ + unsigned code; + Image image; + unsigned x, y; + int offset_x; + int offset_y; + int advance; +} Glyph; + +typedef struct sFont +{ + unsigned size; + int ascent; + int descent; + unsigned n_glyphs; + Glyph *glyphs; + Image image; +} Font; unsigned round_to_pot(unsigned); void usage(); -int save_defs(const char *, const GlyphDef *, int, int, int, int, int, int); -int save_png(const char *, const char *, int, int, char); +void init_font(Font *, FT_Face, unsigned, unsigned, int); +void render_grid(Font *, unsigned, unsigned, int); +void render_packed(Font *); +int save_defs(const char *, const Font *); +int save_png(const char *, const Image *, char); char verbose=0; @@ -47,28 +67,24 @@ int main(int argc, char **argv) int begin=0; int end=255; int size=10; - int cpl=16; - int cell=16; + int cpl=0; + int cell=0; char autohinter=0; char seq=0; char alpha=0; + char pack=0; FT_Library freetype; FT_Face face; - int ch,cw; - int ascent; - int descent; int err; int i; - int count; - int w,h; - char *data; char *out_fn="font.png"; char *def_fn=NULL; - GlyphDef *defs=NULL; + + Font font; if(argc<2) { @@ -76,7 +92,7 @@ int main(int argc, char **argv) return 1; } - while((i=getopt(argc, argv, "r:s:l:c:o:atvh?ed:"))!=-1) + while((i=getopt(argc, argv, "r:s:l:c:o:atvh?ed:p"))!=-1) { char *ptr; int temp; @@ -141,6 +157,9 @@ int main(int argc, char **argv) case 'd': def_fn=optarg; break; + case 'p': + pack=1; + break; } } if(!strcmp(out_fn, "-")) @@ -174,7 +193,7 @@ int main(int argc, char **argv) { const char *name=FT_Get_Postscript_Name(face); printf("Font name: %s\n", name); - printf("Glyphs: %ld\n", face->num_glyphs); + printf("Glyphs: %ld\n", face->num_glyphs); } err=FT_Set_Pixel_Sizes(face, 0, size); @@ -183,116 +202,24 @@ int main(int argc, char **argv) fprintf(stderr, "Couldn't set size\n"); return 1; } - ascent=(face->bbox.yMax*face->size->metrics.y_scale)>>16; - ascent=(ascent+63)/64; - descent=(face->bbox.yMin*face->size->metrics.y_scale)>>16; - descent=(ascent+63)/64; - ch=((face->bbox.yMax-face->bbox.yMin)*face->size->metrics.y_scale)>>16; - ch=(ch+63)/64; - cw=((face->bbox.xMax-face->bbox.xMin)*face->size->metrics.x_scale)>>16; - cw=(cw+63)/64; - if(verbose) - { - printf("Ascent %d\n", ascent); - printf("Descent %d\n", descent); - printf("Max height %d\n", ch); - printf("Max width %d\n", cw); - } - if(verbose>=1 && (ch>cell || cw>cell)) fprintf(stderr,"Warning: character size exceeds cell size\n"); - w=round_to_pot(cpl*cell); - if(seq && face->num_glyphsnum_glyphs+cpl-1)/cpl*cell; + font.size=size; + init_font(&font, face, begin, end, autohinter); + if(pack) + render_packed(&font); else - h=(end-begin+cpl-1)/cpl*cell; - h=round_to_pot(h); - - data=(char *)malloc(w*h); - memset(data, 255, w*h); - + render_grid(&font, cell, cpl, seq); + save_png(out_fn, &font.image, alpha); if(def_fn) - { - if(face->num_glyphsnum_glyphs; - else - count=end-begin; - - defs=(GlyphDef *)malloc(count*sizeof(GlyphDef)); - } + save_defs(def_fn, &font); - count=0; - for(i=begin; i<=end; ++i) - { - int glyph=FT_Get_Char_Index(face,i); - FT_Bitmap *bmp=&face->glyph->bitmap; - int x,y; - int cx,cy; - int flags=0; - int dy; + for(i=0; iglyph, FT_RENDER_MODE_NORMAL); - if(cell>bmp->width) - cx+=(cell-bmp->width)/2; - dy=ascent-face->glyph->bitmap_top; - cy+=dy; - - if(verbose>=2) - { - printf(" Char %d: glyph %d, size %dx%d\n", i, glyph, bmp->width, bmp->rows); - if(bmp->width>cell || dy+bmp->rows>cell || dy<0) printf(" Warning: Character %d does not fit in cell\n", i); - } - - if(bmp->pitch<0) - { - fprintf(stderr, "Warning: Character %d not rendered (can't handle reversed bitmaps)\n", i); - continue; - } - - for(y=0; yrows; ++y) for(x=0; xwidth; ++x) - { - if(cx+x<0 || cx+x>=w || cy+y<0 || cy+y>=h) continue; - data[cx+x+(cy+y)*w]=255-bmp->buffer[x+y*bmp->pitch]; - } - - if(def_fn) - { - defs[count].code=i; - defs[count].x=cx; - defs[count].y=cy; - defs[count].w=bmp->width; - defs[count].h=bmp->rows; - defs[count].offset_x=face->glyph->bitmap_left; - defs[count].offset_y=face->glyph->bitmap_top-bmp->rows; - defs[count].advance=(int)(face->glyph->advance.x+32)/64; - } - - ++count; - } - - while(seq && h/2>=(count+cpl-1)/cpl*cell) - h/=2; - - if(def_fn) - save_defs(def_fn, defs, count, w, h, size, ascent, descent); - save_png(out_fn, data, w, h, alpha); - - if(verbose) printf("Converted %d glyphs\n", count); + FT_Done_Face(face); + FT_Done_FreeType(freetype); return 0; } @@ -312,27 +239,292 @@ unsigned round_to_pot(unsigned n) void usage() { printf("ttf2png - True Type Font to PNG converter\n" - "Copyright (c) 2004 Mikkosoft Productions\n" + "Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions\n" "Distributed under the GNU General Public License\n\n" "Usage: ttf2png [options] \n\n" "Accepted options (default values in [brackets])\n" " -r Range of characters to convert in the format low,high [0,255]\n" " -s Font size to use, in pixels [10]\n" - " -l Number of characters to put in one line [16]\n" - " -c Character cell size, in pixels [16]\n" + " -l Number of characters to put in one line [auto]\n" + " -c Character cell size, in pixels [auto]\n" " -o Output file name (or - for stdout) [font.png]\n" " -a Force autohinter\n" " -t Render font to alpha channel\n" " -v Increase the level of verbosity\n" " -e Use cells in sequence, rather than by code\n" + " -p Pack the glyphs tightly instead of in a grid\n" " -d Write a definition to the given file\n" " -h Print this message\n"); } -int save_defs(const char *fn, const GlyphDef *defs, int count, int w, int h, int size, int ascent, int descent) +void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter) { - FILE *out; - int i; + unsigned i; + unsigned size=0; + + font->ascent=(face->size->metrics.ascender+63)>>6; + font->descent=(face->size->metrics.descender+63)>>6; + + if(verbose>=1) + { + printf("Ascent: %d\n", font->ascent); + printf("Descent: %d\n", font->descent); + } + + font->n_glyphs=0; + font->glyphs=NULL; + for(i=first; i<=last; ++i) + { + unsigned n; + FT_Bitmap *bmp=&face->glyph->bitmap; + int x, y; + int flags=0; + Glyph *glyph; + + n=FT_Get_Char_Index(face, i); + if(!n) continue; + + if(autohinter) + flags|=FT_LOAD_FORCE_AUTOHINT; + FT_Load_Glyph(face, n, flags); + FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + + if(verbose>=2) + printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows); + + if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY) + { + fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n); + continue; + } + + if(font->n_glyphs>=size) + { + size+=16; + font->glyphs=(Glyph *)realloc(font->glyphs, size*sizeof(Glyph)); + } + + glyph=&font->glyphs[font->n_glyphs++]; + glyph->code=i; + glyph->image.w=bmp->width; + glyph->image.h=bmp->rows; + glyph->image.data=(char *)malloc(bmp->width*bmp->rows); + 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; + + if(bmp->pitch<0) + { + for(y=0; yrows; ++y) for(x=0; xwidth; ++x) + glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w]=bmp->buffer[x-y*bmp->pitch]; + } + else + { + for(y=0; yrows; ++y) for(x=0; xwidth; ++x) + glyph->image.data[x+y*glyph->image.w]=bmp->buffer[x+y*bmp->pitch]; + } + } + + if(verbose>=1) + printf("Loaded %u glyphs\n", font->n_glyphs); +} + +void render_grid(Font *font, unsigned cell, unsigned cpl, int seq) +{ + unsigned i; + int top=0, bot=0; + unsigned first, last; + unsigned maxw=0, maxh=0; + + for(i=1;; i<<=1) + { + first=font->glyphs[0].code&~(i-1); + last=first+i-1; + if(last>=font->glyphs[font->n_glyphs-1].code) + break; + } + + for(i=0; in_glyphs; ++i) + { + int y; + + y=font->glyphs[i].offset_y+font->glyphs[i].image.h; + if(y>top) + top=y; + if(font->glyphs[i].offset_yglyphs[i].offset_y; + if(font->glyphs[i].image.w>maxw) + maxw=font->glyphs[i].image.w; + if(font->glyphs[i].image.h>maxh) + maxh=font->glyphs[i].image.h; + } + + if(cell==0) + { + cell=top-bot; + if(maxw>cell) + cell=maxw; + } + + if(verbose>=1) + { + printf("Max size: %u x %u\n", maxw, maxh); + printf("Y range: [%d %d]\n", bot, top); + if(maxw>cell || top-bot>cell) + fprintf(stderr,"Warning: character size exceeds cell size (%d)\n", cell); + } + + if(cpl==0) + { + unsigned count=(seq ? font->n_glyphs : last-first+1); + for(i=1;; i<<=1) + { + cpl=i/cell; + if(cpl>0 && (count+cpl-1)/cpl<=cpl) + break; + } + } + + font->image.w=round_to_pot(cpl*cell); + if(seq && font->n_glyphsimage.h=(font->n_glyphs+cpl-1)/cpl*cell; + else + font->image.h=(last-first+cpl)/cpl*cell; + font->image.h=round_to_pot(font->image.h); + + font->image.data=(char *)malloc(font->image.w*font->image.h); + memset(font->image.data, 255, font->image.w*font->image.h); + + for(i=0; in_glyphs; ++i) + { + Glyph *glyph; + int cx, cy; + unsigned x, y; + + glyph=&font->glyphs[i]; + + if(seq) + { + cx=(i%cpl)*cell; + cy=(i/cpl)*cell; + } + else + { + cx=((glyph->code-first)%cpl)*cell; + cy=((glyph->code-first)/cpl)*cell; + } + + if(cell>glyph->image.w) + cx+=(cell-glyph->image.w)/2; + cy+=top-glyph->offset_y-glyph->image.h; + + glyph->x=cx; + glyph->y=cy; + + for(y=0; yimage.h; ++y) for(x=0; ximage.w; ++x) + { + if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || 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]; + } + } +} + +void render_packed(Font *font) +{ + unsigned i; + unsigned area=0; + unsigned last_h=0xFFFF; + char *used_glyphs; + char *used_pixels; + unsigned cx=0, cy; + + for(i=0; in_glyphs; ++i) + area+=(font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1); + + for(font->image.w=1;; font->image.w<<=1) + { + font->image.h=(area*5/4)/font->image.w; + if(font->image.h<=font->image.w) + break; + } + font->image.h=round_to_pot(font->image.h); + + font->image.data=(char *)malloc(font->image.w*font->image.h); + memset(font->image.data, 255, font->image.w*font->image.h); + used_pixels=(char *)malloc(font->image.w*font->image.h); + memset(used_pixels, 0, font->image.w*font->image.h); + used_glyphs=(char *)malloc(font->n_glyphs); + memset(used_glyphs, 0, font->n_glyphs); + + for(cy=0; cyimage.h;) + { + unsigned w; + unsigned x, y; + Glyph *glyph=NULL; + unsigned best_score=0; + + for(; (cximage.w && used_pixels[cx+cy*font->image.w]); ++cx); + if(cx>=font->image.w) + { + cx=0; + ++cy; + last_h=0xFFFF; + continue; + } + for(w=0; (cx+wimage.w && !used_pixels[cx+w+cy*font->image.w]); ++w); + + for(i=0; in_glyphs; ++i) + { + Glyph *g; + + g=&font->glyphs[i]; + if(!used_glyphs[i] && g->image.w<=w) + { + unsigned score; + + score=g->image.h+1; + if(g->image.h==last_h) + score*=g->image.w; + else + score+=g->image.w; + + if(score>best_score) + { + glyph=g; + best_score=score; + } + } + } + + if(!glyph) + { + cx+=w; + continue; + } + + used_glyphs[glyph-font->glyphs]=1; + glyph->x=cx; + glyph->y=cy; + + for(y=0; yimage.h; ++y) for(x=0; ximage.w; ++x) + { + if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || 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]; + } + for(y=0; yimage.h+2; ++y) for(x=0; ximage.w+2; ++x) + { + if(cx+x<1 || cx+x>font->image.w || cy+y<1 || cy+y>font->image.h) continue; + used_pixels[cx+x-1+(cy+y-1)*font->image.w]=1; + } + + last_h=glyph->image.h; + } +} + +int save_defs(const char *fn, const Font *font) +{ + FILE *out; + unsigned i; out=fopen(fn, "w"); if(!out) @@ -341,11 +533,11 @@ int save_defs(const char *fn, const GlyphDef *defs, int count, int w, int h, int return -1; } - fprintf(out, "%d %d %d %d %d\n", w, h, size, ascent, descent); - for(i=0; iimage.w, font->image.h, font->size, font->ascent, font->descent); + for(i=0; in_glyphs; ++i) { - const GlyphDef *d=defs+i; - fprintf(out, "%d %d %d %d %d %d %d %d\n", d->code, d->x, d->y, d->w, d->h, d->offset_x, d->offset_y, d->advance); + const Glyph *g=&font->glyphs[i]; + fprintf(out, "%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); } fclose(out); @@ -353,12 +545,12 @@ int save_defs(const char *fn, const GlyphDef *defs, int count, int w, int h, int return 0; } -int save_png(const char *fn, const char *data, int w, int h, char alpha) +int save_png(const char *fn, const Image *image, char alpha) { FILE *out; png_struct *pngs; png_info *pngi; - png_byte *rows[h]; + png_byte *rows[image->h]; int i; png_byte *data2; int color; @@ -392,29 +584,32 @@ int save_png(const char *fn, const char *data, int w, int h, char alpha) png_init_io(pngs, out); if(alpha) { - data2=(png_byte *)malloc(w*h*2); - for(i=0; iw*image->h*2); + for(i=0; iw*image->h; ++i) { data2[i*2]=255; - data2[i*2+1]=255-data[i]; + data2[i*2+1]=255-image->data[i]; } - for(i=0; ih; ++i) + rows[i]=(png_byte *)(data2+i*image->w*2); color=PNG_COLOR_TYPE_GRAY_ALPHA; } else { - for(i=0; ih; ++i) + rows[i]=(png_byte *)(image->data+i*image->w); color=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_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); png_destroy_write_struct(&pngs, &pngi); - if(alpha) free(data2); + if(alpha) + free(data2); - if(verbose) printf("Saved %dx%d PNG image to %s\n", w, h, fn); + if(verbose) printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn); + + fclose(out); return 0; } -- 2.43.0