X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=ttf2png.c;h=62e56acfda28413f91c4b978d3e8a682e385be91;hb=ef96a56a5d4a213c8f00ccfdabe64fd32a98b8f1;hp=7cd94551202ff74897f2687a0d8dc597adc1b5ae;hpb=f80db26b7c5db7c120f5b38defa15fed232f6e96;p=ttf2png.git diff --git a/ttf2png.c b/ttf2png.c index 7cd9455..62e56ac 100644 --- a/ttf2png.c +++ b/ttf2png.c @@ -61,18 +61,28 @@ typedef struct sFont Image image; } Font; +typedef struct sRange +{ + unsigned first; + unsigned last; +} Range; + typedef int bool; void usage(void); int convert_numeric_option(char, int); -void convert_code_point_range(char, unsigned *, unsigned *); +void convert_code_point_range(char, Range *); unsigned str_to_code_point(const char *, char **); 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, unsigned, unsigned, bool); -int render_grid(Font *, unsigned, unsigned, unsigned, bool); -int render_packed(Font *, unsigned, unsigned); +int init_font(Font *, FT_Face, const Range *, unsigned, bool); +int init_glyphs(Font *, FT_Face, const Range *, bool); +int copy_bitmap(const FT_Bitmap *, Image *); +int render_grid(Font *, unsigned, unsigned, unsigned, bool, bool); +int render_packed(Font *, unsigned, unsigned, bool); int save_defs(const char *, const Font *); int save_png(const char *, const Image *, char); @@ -81,8 +91,8 @@ char verbose = 0; int main(int argc, char **argv) { char *fn; - unsigned begin = 0; - unsigned end = 255; + Range *ranges = NULL; + unsigned n_ranges = 0; unsigned size = 10; unsigned cpl = 0; unsigned cellw = 0; @@ -94,6 +104,7 @@ int main(int argc, char **argv) bool pack = 0; unsigned margin = 0; unsigned padding = 1; + bool npot = 0; FT_Library freetype; FT_Face face; @@ -112,12 +123,13 @@ int main(int argc, char **argv) return 1; } - while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1) + while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:f")) != -1) { switch(i) { case 'r': - convert_code_point_range('r', &begin, &end); + ranges = (Range *)realloc(ranges, (++n_ranges)*sizeof(Range)); + convert_code_point_range('r', &ranges[n_ranges-1]); break; case 's': size = convert_numeric_option('s', 1); @@ -162,6 +174,9 @@ int main(int argc, char **argv) case 'n': padding = convert_numeric_option('n', 0); break; + case 'f': + npot = 1; + break; } } if(!strcmp(out_fn, "-")) @@ -205,8 +220,18 @@ int main(int argc, char **argv) return 1; } + if(!n_ranges) + { + ranges = malloc(sizeof(Range)); + ranges[0].first = 0; + ranges[0].last = 255; + n_ranges = 1; + } + else + sort_and_compact_ranges(ranges, &n_ranges); + font.size = size; - err = init_font(&font, face, begin, end, autohinter); + err = init_font(&font, face, ranges, n_ranges, autohinter); if(err) return 1; @@ -217,9 +242,9 @@ int main(int argc, char **argv) } if(pack) - err = render_packed(&font, margin, padding); + err = render_packed(&font, margin, padding, npot); else - err = render_grid(&font, cellw, cellh, cpl, seq); + err = render_grid(&font, cellw, cellh, cpl, seq, npot); if(err) return 1; @@ -240,6 +265,7 @@ int main(int argc, char **argv) free(font.glyphs); free(font.kerning); free(font.image.data); + free(ranges); FT_Done_Face(face); FT_Done_FreeType(freetype); @@ -269,6 +295,7 @@ 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" " -d File name for writing glyph definitions\n" " -h Print this message\n"); } @@ -288,26 +315,26 @@ int convert_numeric_option(char opt, int min_value) return value; } -void convert_code_point_range(char opt, unsigned *begin, unsigned *end) +void convert_code_point_range(char opt, Range *range) { int value; char *ptr; if(!strcmp(optarg, "all")) { - *begin = 0; - *end = 0x10FFFF; + range->first = 0; + range->last = 0x10FFFF; return; } value = str_to_code_point(optarg, &ptr); if(value>0 && *ptr==',') { - *begin = value; + range->first = value; value = str_to_code_point(ptr+1, &ptr); if(value>0 && !*ptr) { - *end = value; + range->last = value; return; } } @@ -396,6 +423,44 @@ void convert_size(char opt, unsigned *width, unsigned *height) exit(1); } +void sort_and_compact_ranges(Range *ranges, unsigned *n_ranges) +{ + unsigned i, j; + + if(!*n_ranges) + return; + + qsort(ranges, *n_ranges, sizeof(Range), &range_cmp); + for(i=0, j=1; j<*n_ranges; ++j) + { + if(ranges[i].last+1>=ranges[j].first) + { + if(ranges[j].last>ranges[i].last) + ranges[i].last = ranges[j].last; + } + else + { + ++i; + if(i!=j) + ranges[i] = ranges[j]; + } + } + + *n_ranges = i+1; +} + +int range_cmp(const void *p1, const void *p2) +{ + const Range *r1 = (const Range *)p1; + const Range *r2 = (const Range *)p2; + if(r1->first!=r2->first) + return (r1->firstfirst ? -1 : 1); + else if(r1->last!=r2->last) + return (r1->lastlast ? -1 : 1); + else + return 0; +} + unsigned round_to_pot(unsigned n) { n -= 1; @@ -432,13 +497,13 @@ void *alloc_image_data(size_t a, size_t b) return ptr; } -int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool autohinter) +int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter) { unsigned i, j; unsigned size = 0; - font->ascent = (face->size->metrics.ascender+63)>>6; - font->descent = (face->size->metrics.descender+63)>>6; + font->ascent = (face->size->metrics.ascender+63)/64; + font->descent = (face->size->metrics.descender-63)/64; if(verbose>=1) { @@ -448,11 +513,55 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto font->n_glyphs = 0; font->glyphs = NULL; - for(i=first; i<=last; ++i) + for(i=0; i=1) + printf("Loaded %u glyphs\n", font->n_glyphs); + + font->n_kerning = 0; + font->kerning = NULL; + for(i=0; in_glyphs; ++i) for(j=0; jn_glyphs; ++j) + if(j!=i) + { + FT_Vector kerning; + FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning); + + /* FreeType documentation says that vertical kerning is practically + never used, so we ignore it. */ + if(kerning.x) + { + Kerning *kern; + + if(font->n_kerning>=size) + { + size += 16; + font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning)); + } + + kern = &font->kerning[font->n_kerning++]; + kern->left_code = font->glyphs[i].code; + kern->right_code = font->glyphs[j].code; + kern->distance = kerning.x/64; + } + } + + if(verbose>=1) + printf("Loaded %d kerning pairs\n", font->n_kerning); + + return 0; +} + +int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter) +{ + unsigned i, j; + unsigned size = font->n_glyphs; + + for(i=range->first; i<=range->last; ++i) { unsigned n; FT_Bitmap *bmp = &face->glyph->bitmap; - unsigned x, y; int flags = 0; Glyph *glyph; @@ -466,11 +575,29 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto 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); + { + printf(" Code point U+%04X", i); + if(i>=0x20 && i<0x7F) + printf(" (%c)", i); + else if(i>=0xA0 && i<=0x10FFFF) + { + char utf8[5]; + unsigned bytes; + + for(bytes=2; i>>(1+bytes*5); ++bytes) ; + for(j=0; j>((bytes-j-1)*6))&0x3F); + utf8[0] |= 0xF0<<(4-bytes); + utf8[j] = 0; - if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY) + printf(" (%s)", utf8); + } + printf(": glyph %u, size %dx%d\n", n, bmp->width, bmp->rows); + } + + if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY && bmp->pixel_mode!=FT_PIXEL_MODE_MONO) { - fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n); + fprintf(stderr, "Warning: Glyph %u skipped, incompatible pixel mode\n", n); continue; } @@ -483,14 +610,6 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto glyph = &font->glyphs[font->n_glyphs++]; glyph->index = n; glyph->code = i; - glyph->image.w = bmp->width; - glyph->image.h = bmp->rows; - glyph->image.data = (char *)malloc(bmp->width*bmp->rows); - if(!glyph->image.data) - { - fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows); - return -1; - } 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; @@ -498,56 +617,61 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto /* 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(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(copy_bitmap(bmp, &glyph->image)) + return -1; } - if(verbose>=1) - printf("Loaded %u glyphs\n", font->n_glyphs); + return 0; +} - size = 0; - font->n_kerning = 0; - font->kerning = NULL; - for(i=0; in_glyphs; ++i) for(j=0; jn_glyphs; ++j) - if(j!=i) - { - FT_Vector kerning; - FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning); +int copy_bitmap(const FT_Bitmap *bmp, Image *image) +{ + unsigned x, y; + unsigned char *src; + char *dst; - /* FreeType documentation says that vertical kerning is practically - never used, so we ignore it. */ - if(kerning.x) - { - Kerning *kern; + image->w = bmp->width; + image->h = bmp->rows; + if(!image->w || !image->h) + { + image->data = NULL; + return 0; + } - if(font->n_kerning>=size) - { - size += 16; - font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning)); - } + image->data = (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; + } - kern = &font->kerning[font->n_kerning++]; - kern->left_code = font->glyphs[i].code; - kern->right_code = font->glyphs[j].code; - kern->distance = kerning.x/64; - } + if(bmp->pitch<0) + src = bmp->buffer+(bmp->rows-1)*-bmp->pitch; + else + src = bmp->buffer; + dst = image->data; + + for(y=0; yrows; ++y) + { + if(bmp->pixel_mode==FT_PIXEL_MODE_MONO) + { + for(x=0; xwidth; ++x) + dst[x] = ((src[x/8]&(0x80>>(x%8))) ? 0xFF : 0x00); + } + else + { + for(x=0; xwidth; ++x) + dst[x] = src[x]; } - if(verbose>=1) - printf("Loaded %d kerning pairs\n", font->n_kerning); + src += bmp->pitch; + dst += image->w; + } return 0; } -int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq) +int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq, bool npot) { unsigned i; int top = 0, bot = 0; @@ -611,11 +735,15 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s first -= first%cpl; last = font->glyphs[font->n_glyphs-1].code; - font->image.w = round_to_pot(cpl*cellw); + font->image.w = cpl*cellw; + if(!npot) + font->image.w = round_to_pot(font->image.w); if(seq) - font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh); + font->image.h = (font->n_glyphs+cpl-1)/cpl*cellh; else - font->image.h = round_to_pot((last-first+cpl)/cpl*cellh); + font->image.h = (last-first+cpl)/cpl*cellh; + if(!npot) + font->image.h = round_to_pot(font->image.h); font->image.data = (char *)alloc_image_data(font->image.w, font->image.h); if(!font->image.data) @@ -656,7 +784,7 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s return 0; } -int render_packed(Font *font, unsigned margin, unsigned padding) +int render_packed(Font *font, unsigned margin, unsigned padding, bool npot) { unsigned i; size_t area = 0; @@ -687,7 +815,8 @@ int render_packed(Font *font, unsigned margin, unsigned padding) if(font->image.h<=font->image.w) break; } - font->image.h = round_to_pot(font->image.h); + if(!npot) + font->image.h = round_to_pot(font->image.h); /* 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 @@ -783,7 +912,9 @@ int render_packed(Font *font, unsigned margin, unsigned padding) /* Trim the image to the actually used size, in case the original estimate was too pessimistic. */ - font->image.h = round_to_pot(used_h); + font->image.h = used_h; + if(!npot) + font->image.h = round_to_pot(font->image.h); free(used_glyphs); free(used_pixels);