/* render true type fonts to fb0 * * (w) by stepan@suse.de, code reused from SDL_ttf * */ #include #include #include #include #include #include #include FT_FREETYPE_H #include #include #include #include "ttf.h" #define DEFAULT_PTSIZE 18 /* FIXME: Right now we assume the gray-scale renderer Freetype is using * supports 256 shades of gray, but we should instead key off of num_grays * in the result FT_Bitmap after the FT_Render_Glyph() call. */ #define NUM_GRAYS 256 extern unsigned int fbbytes, fbx, fby, fblinelen, alpha; extern unsigned char *framebuffer; extern unsigned int fbypos, fbxpos; unsigned char* TTF_RenderUNICODE_Shaded( TTF_Font* font, const unsigned short * text, unsigned int fg, unsigned int bg); /* Cached glyph information */ typedef struct cached_glyph { int stored; FT_UInt index; FT_Bitmap bitmap; FT_Bitmap pixmap; int minx; int maxx; int miny; int maxy; int yoffset; int advance; unsigned short cached; } c_glyph; struct _TTF_Font { /* Freetype2 maintains all sorts of useful info itself */ FT_Face face; /* We'll cache these ourselves */ int height; int ascent; int descent; int lineskip; /* The font style */ int style; /* Extra width in glyph bounds for text styles */ int glyph_overhang; float glyph_italics; /* Information in the font for underlining */ int underline_offset; int underline_height; /* Cache for style-transformed glyphs */ c_glyph *current; c_glyph cache[256]; c_glyph scratch; }; static void Flush_Glyph( c_glyph* glyph ); static void Flush_Cache( TTF_Font* font ) { int i; int size = sizeof( font->cache ) / sizeof( font->cache[0] ); for( i = 0; i < size; ++i ) { if( font->cache[i].cached ) { Flush_Glyph( &font->cache[i] ); } } if( font->scratch.cached ) { Flush_Glyph( &font->scratch ); } } /* character conversion */ /* Macro to convert a character to a Unicode value -- assume already Unicode */ #define UNICODE(c) c static unsigned short *ASCII_to_UNICODE(unsigned short *unicode, const char *text, int len) { int i; for ( i=0; i < len; ++i ) { unicode[i] = ((const unsigned char *)text)[i]; } unicode[i] = 0; return unicode; } static unsigned short *UTF8_to_UNICODE(unsigned short *unicode, const char *utf8, int len) { int i, j; unsigned short ch; for ( i=0, j=0; i < len; ++i, ++j ) { ch = ((const unsigned char *)utf8)[i]; if ( ch >= 0xF0 ) { ch = (unsigned short)(utf8[i]&0x07) << 18; ch |= (unsigned short)(utf8[++i]&0x3F) << 12; ch |= (unsigned short)(utf8[++i]&0x3F) << 6; ch |= (unsigned short)(utf8[++i]&0x3F); } else if ( ch >= 0xE0 ) { ch = (unsigned short)(utf8[i]&0x3F) << 12; ch |= (unsigned short)(utf8[++i]&0x3F) << 6; ch |= (unsigned short)(utf8[++i]&0x3F); } else if ( ch >= 0xC0 ) { ch = (unsigned short)(utf8[i]&0x3F) << 6; ch |= (unsigned short)(utf8[++i]&0x3F); } unicode[j] = ch; } unicode[j] = 0; return unicode; } /* TTF stuff */ static FT_Library library; static int TTF_initialized = 0; int TTF_Init( void ) { int status; FT_Error error; status = 0; error = FT_Init_FreeType( &library ); if ( error ) { fprintf(stderr, "Couldn't init FreeType engine %d\n", error ); status = -1; } else { TTF_initialized = 1; } return status; } void TTF_Quit( void ) { if ( TTF_initialized ) { FT_Done_FreeType( library ); } TTF_initialized = 0; } #if 0 SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg) { SDL_Surface *textbuf; Uint16 *unicode_text; int unicode_len; /* Copy the UTF-8 text to a UNICODE text buffer */ unicode_len = strlen(text); unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text)); if ( unicode_text == NULL ) { TTF_SetError("Out of memory"); return(NULL); } UTF8_to_UNICODE(unicode_text, text, unicode_len); RenderUnicode(font, unicode_text, fg); /* Render the new text */ textbuf = TTF_RenderUNICODE_Solid(font, unicode_text, fg); /* Free the text buffer and return */ free(unicode_text); return(textbuf); } #endif unsigned char*TTF_RenderText_Shaded(TTF_Font *font, const char *text, unsigned int fg, unsigned int bg) { unsigned char *textbuf; unsigned short *unicode_text; int unicode_len; /* Copy the UTF-8 text to a UNICODE text buffer */ unicode_len = strlen(text); unicode_text = (unsigned short *)malloc((unicode_len+1)*(sizeof *unicode_text)); if ( unicode_text == NULL ) { printf("Out of memory\n"); return(NULL); } UTF8_to_UNICODE(unicode_text, text, unicode_len); /* Render the new text */ textbuf = TTF_RenderUNICODE_Shaded(font, unicode_text, fg, bg); /* Free the text buffer and return */ free(unicode_text); return(textbuf); } void TTF_CloseFont( TTF_Font* font ) { Flush_Cache( font ); FT_Done_Face( font->face ); free( font ); } void TTF_SetFontStyle( TTF_Font* font, int style ) { font->style = style; Flush_Cache( font ); } TTF_Font* TTF_OpenFontIndex( const char *file, int ptsize, long index ) { TTF_Font* font; FT_Error error; FT_Face face; FT_Fixed scale; extern int strict_font; font = (TTF_Font*) malloc(sizeof *font); if ( font == NULL ) { fprintf(stderr, "Out of memory\n" ); return NULL; } memset( font, 0, sizeof( *font ) ); /* Open the font and create ancillary data */ error = FT_New_Face( library, file, 0, &font->face ); if( error && !strict_font ) error=FT_New_Memory_Face(library, (const FT_Byte*)luxisri_ttf, LUXISRI_SIZE, 0, &font->face ); if( error ) { printf( "Couldn't load font file\n"); free( font ); return NULL; } if ( index != 0 ) { if ( font->face->num_faces > index ) { FT_Done_Face( font->face ); error = FT_New_Face( library, file, index, &font->face ); if( error ) { printf( "Couldn't get font face\n"); free( font ); return NULL; } } else { fprintf(stderr, "No such font face\n"); free( font ); return NULL; } } face = font->face; /* Make sure that our font face is scalable (global metrics) */ if ( ! FT_IS_SCALABLE(face) ) { fprintf(stderr,"Font face is not scalable\n"); TTF_CloseFont( font ); return NULL; } /* Set the character size and use default DPI (72) */ error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 0, 0 ); if( error ) { fprintf(stderr, "Couldn't set font size\n"); TTF_CloseFont( font ); return NULL; } /* Get the scalable font metrics for this font */ scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale)); font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale)); font->height = font->ascent - font->descent + /* baseline */ 1; font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); if ( font->underline_height < 1 ) { font->underline_height = 1; } #ifdef DEBUG_FONTS printf("Font metrics:\n"); printf("\tascent = %d, descent = %d\n", font->ascent, font->descent); printf("\theight = %d, lineskip = %d\n", font->height, font->lineskip); printf("\tunderline_offset = %d, underline_height = %d\n", font->underline_offset, font->underline_height); #endif /* Set the default font style */ font->style = TTF_STYLE_NORMAL; font->glyph_overhang = face->size->metrics.y_ppem / 10; /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ font->glyph_italics = 0.207f; font->glyph_italics *= font->height; return font; } TTF_Font* TTF_OpenFont( const char *file, int ptsize ) { return TTF_OpenFontIndex(file, ptsize, 0); } static void Flush_Glyph( c_glyph* glyph ) { glyph->stored = 0; glyph->index = 0; if( glyph->bitmap.buffer ) { free( glyph->bitmap.buffer ); glyph->bitmap.buffer = 0; } if( glyph->pixmap.buffer ) { free( glyph->pixmap.buffer ); glyph->pixmap.buffer = 0; } glyph->cached = 0; } static FT_Error Load_Glyph( TTF_Font* font, unsigned short ch, c_glyph* cached, int want) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics* metrics; FT_Outline* outline; assert( font ); assert( font->face ); face = font->face; /* Load the glyph */ if ( ! cached->index ) { cached->index = FT_Get_Char_Index( face, ch ); } error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT ); if( error ) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(metrics->height); cached->yoffset = font->ascent - cached->maxy; cached->advance = FT_CEIL(metrics->horiAdvance); /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { cached->maxx += font->glyph_overhang; } if( font->style & TTF_STYLE_ITALIC ) { cached->maxx += (int)ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap* src; FT_Bitmap* dst; /* Handle the italic style */ if( font->style & TTF_STYLE_ITALIC ) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform( outline, &shear ); } /* Render the glyph */ if ( mono ) { error = FT_Render_Glyph( glyph, ft_render_mode_mono ); } else { error = FT_Render_Glyph( glyph, ft_render_mode_normal ); } if( error ) { return error; } /* Copy over information to cache */ src = &glyph->bitmap; if ( mono ) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy( dst, src, sizeof( *dst ) ); if ( mono ) { dst->pitch *= 8; } /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if( font->style & TTF_STYLE_ITALIC ) { int bump = (int)ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if (dst->rows != 0) { dst->buffer = malloc( dst->pitch * dst->rows ); if( !dst->buffer ) { return FT_Err_Out_Of_Memory; } memset( dst->buffer, 0, dst->pitch * dst->rows ); for( i = 0; i < src->rows; i++ ) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if ( mono ) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; for ( j = 0; j < src->width; j += 8 ) { unsigned char ch = *srcp++; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; } } else { memcpy(dst->buffer+doffset, src->buffer+soffset, src->pitch); } } } /* Handle the bold style */ if ( font->style & TTF_STYLE_BOLD ) { int row; int col; int offset; int pixel; unsigned char* pixmap; /* The pixmap is a little hard, we have to add and clamp */ for( row = dst->rows - 1; row >= 0; --row ) { pixmap = (unsigned char*) dst->buffer + row * dst->pitch; for( offset=1; offset <= font->glyph_overhang; ++offset ) { for( col = dst->width - 1; col > 0; --col ) { pixel = (pixmap[col] + pixmap[col-1]); if( pixel > NUM_GRAYS - 1 ) { pixel = NUM_GRAYS - 1; } pixmap[col] = (unsigned char) pixel; } } } } /* Mark that we rendered this format */ if ( mono ) { cached->stored |= CACHED_BITMAP; } else { cached->stored |= CACHED_PIXMAP; } } /* We're done, mark this glyph cached */ cached->cached = ch; return 0; } static FT_Error Find_Glyph( TTF_Font* font, unsigned short ch, int want ) { int retval = 0; if( ch < 256 ) { font->current = &font->cache[ch]; } else { if ( font->scratch.cached != ch ) { Flush_Glyph( &font->scratch ); } font->current = &font->scratch; } if ( (font->current->stored & want) != want ) { retval = Load_Glyph( font, ch, font->current, want ); } return retval; } int TTF_SizeUNICODE(TTF_Font *font, const unsigned short *text, int *w, int *h) { int status; const unsigned short *ch; int x, z; int minx, maxx; int miny, maxy; c_glyph *glyph; FT_Error error; /* Initialize everything to 0 */ if ( ! TTF_initialized ) { return -1; } status = 0; minx = maxx = 0; miny = maxy = 0; /* Load each character and sum it's bounding box */ x= 0; for ( ch=text; *ch; ++ch ) { error = Find_Glyph(font, *ch, CACHED_METRICS); if ( error ) { return -1; } glyph = font->current; z = x + glyph->minx; if ( minx > z ) { minx = z; } if ( font->style & TTF_STYLE_BOLD ) { x += font->glyph_overhang; } if ( glyph->advance > glyph->maxx ) { z = x + glyph->advance; } else { z = x + glyph->maxx; } if ( maxx < z ) { maxx = z; } x += glyph->advance; if ( glyph->miny < miny ) { miny = glyph->miny; } if ( glyph->maxy > maxy ) { maxy = glyph->maxy; } } /* Fill the bounds rectangle */ if ( w ) { *w = (maxx - minx); } if ( h ) *h = font->height; return status; } unsigned char* TTF_RenderUNICODE_Shaded( TTF_Font* font, const unsigned short * text, unsigned int fg, unsigned int bg ) { int xstart; int width; int height; unsigned char* textbuf; int rdiff; int gdiff; int bdiff; unsigned short val, bgc; const unsigned short* ch; unsigned char* src; unsigned char* dst; int row, col; c_glyph *glyph; FT_Error error; /* Get the dimensions of the text surface */ if( ( TTF_SizeUNICODE(font, text, &width, NULL) < 0 ) || !width ) { fprintf(stderr,"Text has zero width\n"); return NULL; } height = font->height; /* Create the target surface */ textbuf=malloc(width*height*fbbytes); if( textbuf == NULL ) { return NULL; } /* Load and render each character */ xstart = 0; for( ch = text; *ch; ++ch ) { FT_Bitmap* current; error = Find_Glyph(font, *ch, CACHED_METRICS|CACHED_PIXMAP); if( error ) { free(textbuf); return NULL; } glyph = font->current; current = &glyph->pixmap; for( row = 0; row < current->rows; ++row ) { dst = (unsigned char *) framebuffer + (fbypos+row+glyph->yoffset) * fblinelen + (xstart + glyph->minx+fbxpos)*fbbytes; src = current->buffer + row * current->pitch; for ( col=current->width; col>0; --col ) { val=*src++; bgc=*(unsigned short *)dst; /* get color parts from real color */ rdiff=(fg>>16); gdiff=((fg>>8)&0xff); bdiff=(fg&0xff); val=alpha*val/100; /* dim color down to current pixel value */ rdiff=rdiff*val/0xff; gdiff=gdiff*val/0xff; bdiff=bdiff*val/0xff; #if 1 /* do alpha transparency */ //rdiff=(rdiff*alpha/100); rdiff+=((bgc)>>8&0xf8)*(0xff-val)/0xff; //gdiff=(gdiff*alpha/100); gdiff+=((bgc>>3)&0xfc)*(0xff-val)/0xff; //bdiff=(bdiff*alpha/100); bdiff+=((bgc<<3)&0xf8)*(0xff-val)/0xff; #endif val=((rdiff&0xf8)<<8)|((gdiff&0xfc)<<3)|(bdiff>>3); //*dst++ = (val >>8); *dst++ = val&0xff; *dst++ = (val >>8); } //printf("\n"); } xstart += glyph->advance; if( font->style & TTF_STYLE_BOLD ) { xstart += font->glyph_overhang; } } /* Handle the underline style */ if( font->style & TTF_STYLE_UNDERLINE ) { row = font->ascent - font->underline_offset - 1; if ( row >= fby) { row = (height-1) - font->underline_height; } dst = (unsigned char *)textbuf + row * width*fbbytes; for ( row=font->underline_height; row>0; --row ) { memset( dst, NUM_GRAYS - 1, width ); dst += width*fbbytes; } } return textbuf; } int rendertext(char *text, char *fontname, unsigned int ptsize, unsigned int forecol) { TTF_Font *font; int renderstyle=0; enum { RENDER_LATIN1, RENDER_UTF8, RENDER_UNICODE } rendertype; /* Initialize the TTF library */ if ( TTF_Init() < 0 ) { fprintf(stderr, "Couldn't initialize TTF.\n"); return(2); } atexit(TTF_Quit); /* Open the font file with the requested point size */ font = TTF_OpenFont(fontname, ptsize); if ( font == NULL ) { fprintf(stderr, "Couldn't load %d pt font from %s\n", ptsize, fontname ); return(2); } renderstyle = TTF_STYLE_NORMAL; rendertype = RENDER_UTF8; TTF_SetFontStyle(font, renderstyle); text = TTF_RenderText_Shaded(font, text, forecol, 0); return 0; }