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);
}