ref: 561f0f568f969514ada751be1db7201aa6d32ac0
dir: /ttf2subf.c/
#include <ft2build.h> #include FT_FREETYPE_H #include FT_ERRORS_H #include <freetype/ftglyph.h> #include <draw.h> #include <memdraw.h> FT_Library lib; #define FLOOR(x) ((x) & -64) #define CEIL(x) (((x) + 63) & -64) #define TRUNC(x) ((x) >> 6) #define ROUND(x) (((x) + 32) & -64) enum { Rmono = 1, Rantialias, Rsubpixel, }; enum { Srgb = 1, Sbgr, Svrgb, Svbgr }; typedef struct { int left; int right; int top; int bottom; int advance; int width; }FTmetrics; typedef struct _FTsubfont { int nchars; int startc; int ascent; int descent; int image_height; int image_width; FTmetrics* metrics; Fontchar* fchars; Subfont sf; Memimage* image; struct _FTsubfont* next; struct _FTsubfont* prev; }FTsubfont; typedef struct { char* name; int size; FT_Face face; FT_GlyphSlot slot; int rmode; int rgb; int image_channels; int ascent; int descent; int height; FTsubfont* sfont; }FTfont; int f[3] = {1, 2, 3}; int fsum = 9; FT_Vector getScale(FTfont* font) { FT_Vector ret; ret.x = ret.y = 1; if(font->rmode == Rsubpixel){ switch(font->rgb){ case Srgb: case Sbgr: ret.x = 3; break; case Svrgb: case Svbgr: ret.y = 3; break; } } return ret; } FT_Glyph getGlyph(FTfont* font, int c) { int status; FT_Glyph glyph; FT_UInt gidx; FT_Int32 lflags; FT_Vector scale; FT_Vector pos; FT_Matrix tm; gidx = FT_Get_Char_Index(font->face, c); switch(font->rmode){ case Rmono: case Rsubpixel: default: lflags = FT_LOAD_TARGET_MONO; break; case Rantialias: lflags = FT_LOAD_DEFAULT; break; } if((status = FT_Load_Glyph(font->face, gidx, lflags))) sysfatal("FT_Load_Glyph: %s\n", FT_Error_String(status)); if((status = FT_Get_Glyph(font->face->glyph, &glyph))) sysfatal("FT_Get_Glyph: %s\n", FT_Error_String(status)); glyph->advance.x = font->slot->advance.x; glyph->advance.y = font->slot->advance.y; scale = getScale(font); tm.xx = 0x10000 * scale.x; tm.yy = 0x10000 * scale.y; tm.xy = tm.yx = 0; pos.x = pos.y = 0; if((status = FT_Glyph_Transform(glyph, &tm, &pos))) sysfatal("FT_Glyph_Transform: %s\n", FT_Error_String(status)); return glyph; } FT_BitmapGlyph getBitmapGlyph(FTfont* font, FT_Glyph glyph) { int rmode = 0; int status; switch(font->rmode){ case Rmono: rmode = ft_render_mode_mono; break; case Rantialias: case Rsubpixel: rmode = ft_render_mode_normal; break; default: sysfatal("error: rmode != antialias|mono|subpixel"); } if((status = FT_Glyph_To_Bitmap(&glyph, rmode, 0, 1))) sysfatal("FT_Glyph_To_Bitmap: %s\n", FT_Error_String(status)); return (FT_BitmapGlyph)glyph; } FTmetrics getMetrics(FTfont* font, int c) { FT_Glyph glyph; FT_BitmapGlyph bglyph; FT_Bitmap bitmap; FT_Vector scale; FTmetrics ret; glyph = getGlyph(font, c); bglyph = getBitmapGlyph(font, glyph); scale = getScale(font); bitmap = bglyph->bitmap; ret.left = bglyph->left / scale.x; ret.right = (bglyph->left + bitmap.width) / scale.x; ret.top = bglyph->top / scale.y; ret.bottom = (bglyph->top - bitmap.rows) / scale.y; ret.width = bitmap.width / scale.x; ret.advance = TRUNC(font->slot->advance.x); /* usage of slot ??? */ if(font->rmode == Rsubpixel){ switch(font->rgb){ case Srgb: case Sbgr: ret.left -= 1; ret.right += 1; ret.width += 2; break; case Svrgb: case Svbgr: ret.top -= 1; ret.bottom -= 1; break; } } /* FT_Done_Glyph(glyph); */ return ret; } unsigned long getPixelMono(FTfont*, FT_Bitmap* bitmap, int x, int y) { int g = ((bitmap->buffer[bitmap->pitch * y + x / 8]) >> (7 - x % 8) & 1) * 255; return (g << 24) | (g << 16) | (g << 8) | 0xFF; } unsigned long getPixelAntialias(FTfont*, FT_Bitmap* bitmap, int x, int y) { int g = bitmap->buffer[bitmap->pitch * y + x]; return (g << 24) | (g << 16) | (g << 8) | 0xFF; } unsigned long getPixelSubpixel(FTfont* font, FT_Bitmap* bitmap, int x, int y) { int xs = 0, ys = 0; int af = 0, bf = 0, cf = 0; int xx, yy; int h[7]; int a, b, c; FT_Vector scale; int i; scale = getScale(font); switch(font->rgb){ case Srgb: case Sbgr: xs = 1; ys = 0; break; case Svrgb: case Svbgr: xs = 0; ys = 1; break; } switch(font->rgb){ case Srgb: case Svrgb: af = 24; bf = 16; cf = 8; break; case Sbgr: case Svbgr: af = 8; bf = 16; cf = 24; break; } xx = x * scale.x; yy = y * scale.y; for(i = -2; i < 5; i++){ int xc, yc; xc = xx + (i - scale.x) * xs; yc = yy + (i - scale.y) * ys; if(xc < 0 || xc >= bitmap->width || yc < 0 || yc >= bitmap->rows){ h[i + 2] = 0; }else{ h[i + 2] = bitmap->buffer[yc * bitmap->pitch + xc] * 65536; } } a = (h[0] * f[0] + h[1] * f[1] + h[2] * f[2] + h[3] * f[1] + h[4] * f[0]) / fsum; b = (h[1] * f[0] + h[2] * f[1] + h[3] * f[2] + h[4] * f[1] + h[5] * f[0]) / fsum; c = (h[2] * f[0] + h[3] * f[1] + h[4] * f[2] + h[5] * f[1] + h[6] * f[0]) / fsum; return ((a / 65536) << af) | ((b / 65536) << bf) | ((c / 65536) << cf) | 0xFF; } typedef unsigned long (*getpixelfunc)(FTfont*, FT_Bitmap*, int, int); getpixelfunc getPixelFunc(FTfont* font) { getpixelfunc ret = 0; switch(font->rmode){ case Rmono: ret = getPixelMono; break; case Rantialias: ret = getPixelAntialias; break; case Rsubpixel: ret = getPixelSubpixel; break; } return ret; } void drawChar(FTfont* font, Memimage* img, int x0, int y0, int c) { Rectangle r; Memimage* color; FT_Glyph glyph; FT_BitmapGlyph bglyph; FT_Bitmap bitmap; int x, y; getpixelfunc getPixel; FT_Vector scale; int height, width; glyph = getGlyph(font, c); scale = getScale(font); bglyph = getBitmapGlyph(font, glyph); bitmap = bglyph->bitmap; getPixel = getPixelFunc(font); r.min.x = 0; r.min.y = 0; r.max.x = 1; r.max.y = 1; color = allocmemimage(r, font->image_channels); r.min.x = r.min.y = 0; r.max.x = bitmap.width; r.max.y = bitmap.rows; width = bitmap.width / scale.x; height = bitmap.rows / scale.y; if(font->rmode == Rsubpixel){ switch(font->rgb){ case Srgb: case Sbgr: width += 2; break; case Svrgb: case Svbgr: height += 2; break; } } for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ Point pt; unsigned long cl = getPixel(font, &bitmap, x, y); pt.x = x + x0; pt.y = y + y0; memfillcolor(color, cl); memimagedraw(img, rectaddpt(Rect(0, 0, 1, 1), pt), color, ZP, nil, ZP, SatopD); } } freememimage(color); } void initSubfont(FTfont* font, FTsubfont* sf, int startc) { int i; int width; int ascent, descent; FTmetrics rc[257]; // get first defined character while(startc < 65536){ if(startc == 0 || FT_Get_Char_Index(font->face, startc) != 0){ break; } startc++; } ascent = 0; descent = 100000; width = 0; for(i = 0; i < sizeof(rc) / sizeof(rc[0]); i++){ if(startc + i >= 65536){ break; } if(width > 2000){ break; } if(i > 0 && FT_Get_Char_Index(font->face, startc + i) == 0){ rc[i] = getMetrics(font, 0); rc[i].width = 0; rc[i].advance = 0; }else rc[i] = getMetrics(font, startc + i); if(ascent < rc[i].top){ ascent = rc[i].top; } if(descent > rc[i].bottom){ descent = rc[i].bottom; } width += rc[i].width; // +1? } sf->startc = startc; sf->nchars = i; sf->ascent = ascent; sf->descent = descent; sf->image_width = width; sf->metrics = malloc(i * sizeof(FTmetrics)); sf->fchars = malloc((i + 1) * sizeof(Fontchar)); for(i = 0; i < sf->nchars; i++){ sf->metrics[i] = rc[i]; } } int createSubfont(FTfont* font, FTsubfont* sf, char* fname) { int i; Rectangle r; int x; int fd; sf->image_height = sf->ascent - sf->descent; /* print("creating %d: (%d, %d)\n", sf->startc, sf->image_height, sf->image_width); */ r = Rect(0, 0, sf->image_width, font->height /*sf->image_height*/); if(sf->image_width <= 0 || sf->image_height <= 0){ return 1; } sf->image = allocmemimage(r, font->image_channels); memfillcolor(sf->image, 0x000000FF); x = 0; for(i = 0; i < sf->nchars; i++){ sf->fchars[i].x = x; sf->fchars[i].width = sf->metrics[i].advance; sf->fchars[i].left = sf->metrics[i].left; sf->fchars[i].bottom = /*sf->ascent*/ font->ascent - sf->metrics[i].bottom; sf->fchars[i].top = (font->ascent - sf->metrics[i].top) > 0 ? font->ascent - sf->metrics[i].top : 0; x += sf->metrics[i].width; // +1? drawChar(font, sf->image, sf->fchars[i].x, sf->fchars[i].top, i + sf->startc); } sf->fchars[i].x = x; sf->sf.name = font->name; sf->sf.n = sf->nchars; sf->sf.height = font->height; /*sf->image_height; */ sf->sf.ascent = font->ascent /*sf->ascent */; sf->sf.info = sf->fchars; sf->sf.ref = 1; fd = create(fname, OWRITE, 0666); if(fd < 0) sysfatal("can not create font file: %r"); writememimage(fd, sf->image); writesubfont(fd, &sf->sf); close(fd); freememimage(sf->image); sf->image = nil; return 0; } void usage(char* s) { sysfatal( "usage: %s [-h] [-s size] [-f fname] [-n name] [-m 'rendering mode'] [-r 'display " "type']\n" "\t-s size - point size\n" "\t-h - this message\n" "\t-f fname - ttf file name\n" "\t-n name - font name\n" "\t-m mono|antialias|subpixel - rendering mode\n" "\t-r rgb|bgr|vrgb|vbgr - LCD display type\n", s); } void main(int argc, char* argv[]) { FTfont font; int status; int i; FTsubfont* sf; int fd; char buf[256]; char* fname = nil; char* mode = nil; char* srgb = "rgb"; char* progname = argv[0]; font.name = nil; font.size = 0; if(argc == 1) usage(progname); ARGBEGIN { case 's': font.size = atoi(ARGF()); break; case 'h': usage(progname); break; /* never reached */ case 'f': fname = ARGF(); break; case 'n': font.name = ARGF(); break; case 'm': mode = ARGF(); break; case 'r': srgb = ARGF(); break; default: sysfatal("bad flag: %c", ARGC()); } ARGEND if(fname == nil){ print("no font name specified\n"); exits("fname"); } if(font.size == 0){ print("no font size specified\n"); exits("size"); } if(mode != nil){ if(strcmp(mode, "mono") == 0){ font.rmode = Rmono; }else if(strcmp(mode, "antialias") == 0){ font.rmode = Rantialias; }else if(strcmp(mode, "subpixel") == 0){ font.rmode = Rsubpixel; } }else{ font.rmode = Rsubpixel; } switch(font.rmode){ case Rmono: font.image_channels = GREY1; break; case Rantialias: font.image_channels = GREY8; break; case Rsubpixel: font.image_channels = RGB24; if(strcmp(srgb, "rgb") == 0){ font.rgb = Srgb; }else if(strcmp(srgb, "bgr") == 0){ font.rgb = Sbgr; }else if(strcmp(srgb, "vrgb") == 0){ font.rgb = Svrgb; }else if(strcmp(srgb, "vbgr") == 0){ font.rgb = Svbgr; }else{ sysfatal("unknown subpixel mode: %s\n", srgb); } break; } if(font.name == nil){ font.name = fname; } print("font file: %s, font name: %s, size: %d\n", fname, font.name, font.size); memimageinit(); if((status = FT_Init_FreeType(&lib))) sysfatal("FT_Init_FreeType: %s\n", FT_Error_String(status)); if((status = FT_New_Face(lib, fname, 0, &font.face))) sysfatal("FT_New_Face: %s\n", FT_Error_String(status)); if((status = FT_Set_Char_Size(font.face, 0, font.size * 64, 72, 72))) sysfatal("FT_Set_Char_Size: %s\n", FT_Error_String(status)); FT_Select_Charmap(font.face, ft_encoding_unicode); font.slot = font.face->glyph; font.sfont = nil; font.ascent = 0; font.descent = 10000; i = 0; while(i < 65536){ struct _FTsubfont* sf = malloc(sizeof(FTsubfont)); initSubfont(&font, sf, i); if(sf->nchars == 0){ break; } print("last: %d, start: %d, nchars: %d, width: %d\n", i, sf->startc, sf->nchars, sf->image_width); i = sf->startc + sf->nchars; sf->next = font.sfont; font.sfont = sf; if(font.ascent < sf->ascent){ font.ascent = sf->ascent; } if(font.descent > sf->descent){ font.descent = sf->descent; } } /* font.height = font.ascent - font.descent; */ font.height = font.size; font.ascent = font.height - 3; /* do we have a directory created for this font? */ snprint(buf, sizeof(buf), "%s", font.name); if(access(buf, AEXIST) == -1){ fd = create(buf, OREAD, DMDIR | 0777); if(fd < 0) sysfatal("cannot create font directory: %r"); } snprint(buf, sizeof(buf), "%s/%s.%d.font", font.name, font.name, font.size); fd = create(buf, OWRITE, 0666); if(fd < 0) sysfatal("cannot create .font file: %r"); fprint(fd, "%d\t%d\n", font.height, font.ascent); for(sf = font.sfont; sf != nil; sf = sf->next){ snprint(buf, sizeof(buf), "%s/%s.%d.%04x", font.name, font.name, font.size, sf->startc); // printf("generating 0x%04x - 0x%04x\n", sf->startc, sf->startc + // sf->nchars); if(0 == createSubfont(&font, sf, buf)){ snprint(buf, sizeof(buf), "%s.%d.%04x", font.name, font.size, sf->startc); fprint(fd, "0x%04x\t0x%04x\t%s\n", sf->startc, sf->startc + sf->nchars - 1, buf); } } close(fd); }