diff options
author | Gary Wong <gtw@gnu.org> | 2009-08-30 21:30:19 -0600 |
---|---|---|
committer | Gary Wong <gtw@gnu.org> | 2009-08-30 21:30:19 -0600 |
commit | 20e28ea41bc3deae601e5995937c22d5d968cccd (patch) | |
tree | 95ac893cb04081ef1b9d8407950cf46b5cb2ba11 | |
parent | f62ba0d2b29be17191f17c8847036febb71aa79e (diff) |
Add multiple text styles, and multiple faces per style.
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | decorate-render.c | 326 |
2 files changed, 270 insertions, 66 deletions
@@ -1,3 +1,13 @@ +2009-08-30 Gary Wong <gtw@gnu.org> + + * decorate-render.c (lookup_face): New function. + (query_metrics, query_glyph, replace_glyph, render_text): Take style + into consideration. + (render_update_window): Use styles, as appropriate. + (decorate_render_init): Remember fontconfig charsets and patterns + for each face in each style. + (decorate_render_done): Clean up fontconfig charsets and patterns. + 2009-08-29 Gary Wong <gtw@gnu.org> * frame.c (build_edges): Use xmalloc() instead of malloc(). diff --git a/decorate-render.c b/decorate-render.c index e8fb9d6..57dfa9b 100644 --- a/decorate-render.c +++ b/decorate-render.c @@ -27,7 +27,7 @@ #include <fontconfig/fontconfig.h> #include <ft2build.h> #include FT_FREETYPE_H -#include FT_CACHE_H +#include FT_BITMAP_H #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -38,15 +38,39 @@ #include "decorate-render.h" -/* FIXME make this configurable */ -#define FONT_SIZE 12 +enum style_id { + STYLE_TITLE, STYLE_MENU, NUM_STYLES +}; + +#define TITLE_FONT_SIZE 12 +#define MENU_FONT_SIZE 10 + #define STRING2(x) #x #define STRING(x) STRING2(x) -#define FONT_SIZE_STRING STRING( FONT_SIZE ) -#define FONT_NAME "sans:pixelsize=" FONT_SIZE_STRING ":bold" +#define TITLE_FONT_SIZE_STRING STRING( TITLE_FONT_SIZE ) +#define MENU_FONT_SIZE_STRING STRING( MENU_FONT_SIZE ) + +static const FcChar8 *const style_names[ NUM_STYLES ] = { + /* FIXME make this configurable */ + (FcChar8 *) "sans:pixelsize=" TITLE_FONT_SIZE_STRING ":bold", + (FcChar8 *) "sans:pixelsize=" MENU_FONT_SIZE_STRING +}; + +static struct font { + FcPattern *pattern; + FcCharSet *charset; + FT_Face face; + FT_Int32 load_flags; +} *fonts; +static int num_fonts; + +static struct style { + int *fonts; /* indices into font table above */ + int num_fonts; + FcCharSet *charset; +} styles[ NUM_STYLES ]; static FT_Library ftl; -static FT_Face ftf; /* FIXME allow multiple faces per fontconfig pattern */ static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = { { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */ @@ -75,25 +99,124 @@ static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = { #endif static struct metric_cache_line { + enum style_id style; uint32_t c; /* character code */ unsigned int time; int x_off, y_off; } metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ]; static struct glyph_cache_line { + enum style_id style; uint32_t c; /* character code */ unsigned int time; } glyph_cache[ GLYPH_CACHE_SIZE ][ GLYPH_CACHE_ASSOC ]; static unsigned int current_time; -static void query_metrics( uint32_t c, int *x_off, int *y_off ) { +static struct font *lookup_font( enum style_id style, uint32_t c ) { + + int i; + + if( !FcCharSetHasChar( styles[ style ].charset, c ) ) + return NULL; + + for( i = 0; i < styles[ style ].num_fonts; i++ ) { + struct font *font = fonts + styles[ style ].fonts[ i ]; + + if( FcCharSetHasChar( font->charset, c ) ) { + if( !font->face ) { + FcChar8 *filename; + FcMatrix *matrix = NULL; + FT_Matrix ft_matrix; + int index = 0, hintstyle = FC_HINT_MEDIUM; + double size; + FcBool hinting = TRUE, autohint = FALSE, globaladvance = TRUE, + embeddedbitmap = TRUE; + + if( FcPatternGetString( font->pattern, FC_FILE, 0, + &filename ) || + FcPatternGetDouble( font->pattern, FC_PIXEL_SIZE, 0, + &size ) ) + continue; + + FcPatternGetInteger( font->pattern, FC_INDEX, 0, &index ); + FcPatternGetMatrix( font->pattern, FC_MATRIX, 0, &matrix ); + + if( FT_New_Face( ftl, (char *) filename, index, &font->face ) ) + continue; + + FT_Set_Pixel_Sizes( font->face, 0, (int) ( size + 0.5 ) ); + + if( matrix ) { + ft_matrix.xx = matrix->xx * 64.0; + ft_matrix.xy = matrix->xy * 64.0; + ft_matrix.yx = matrix->yx * 64.0; + ft_matrix.yy = matrix->yy * 64.0; + FT_Set_Transform( font->face, &ft_matrix, NULL ); + } + + FT_Select_Charmap( font->face, FT_ENCODING_UNICODE ); + + FcPatternGetBool( font->pattern, FC_HINTING, 0, &hinting ); + FcPatternGetInteger( font->pattern, FC_HINT_STYLE, 0, + &hintstyle ); + FcPatternGetBool( font->pattern, FC_AUTOHINT, 0, &autohint ); + FcPatternGetBool( font->pattern, FC_GLOBAL_ADVANCE, 0, + &globaladvance ); + FcPatternGetBool( font->pattern, FC_EMBEDDED_BITMAP, 0, + &embeddedbitmap ); + + font->load_flags = FT_LOAD_DEFAULT; + + if( !hinting ) + font->load_flags |= FT_LOAD_NO_HINTING; + + switch( hintstyle ) { + case FC_HINT_NONE: + font->load_flags |= FT_LOAD_NO_HINTING; + break; + + case FC_HINT_SLIGHT: + font->load_flags |= FT_LOAD_TARGET_LIGHT; + break; + + case FC_HINT_MEDIUM: + default: + font->load_flags |= FT_LOAD_TARGET_NORMAL; + break; + + case FC_HINT_FULL: + font->load_flags |= FT_LOAD_TARGET_MONO; + break; + } + + if( autohint ) + font->load_flags |= FT_LOAD_FORCE_AUTOHINT; + + if( !globaladvance ) + font->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + + if( !embeddedbitmap ) + font->load_flags |= FT_LOAD_NO_BITMAP; + } + + return font; + } + } + + return NULL; +} + +static void query_metrics( enum style_id style, uint32_t c, + int *x_off, int *y_off ) { int row = c & ( METRIC_CACHE_SIZE - 1 ); int i, max; - + struct font *font; + for( i = 0; i < METRIC_CACHE_ASSOC; i++ ) - if( metric_cache[ row ][ i ].c == c ) { + if( metric_cache[ row ][ i ].style == style && + metric_cache[ row ][ i ].c == c ) { /* Cache hit. */ metric_cache[ row ][ i ].time = current_time; *x_off = metric_cache[ row ][ i ].x_off; @@ -101,8 +224,10 @@ static void query_metrics( uint32_t c, int *x_off, int *y_off ) { return; } + font = lookup_font( style, c ); + /* Cache miss. */ - if( FT_Load_Char( ftf, c, FT_LOAD_DEFAULT ) ) { + if( FT_Load_Char( font->face, c, font->load_flags ) ) { /* Couldn't load metrics. Don't bother evicting anything. */ *x_off = *y_off = 0; return; @@ -114,21 +239,23 @@ static void query_metrics( uint32_t c, int *x_off, int *y_off ) { current_time - metric_cache[ row ][ max ].time ) max = i; + metric_cache[ row ][ max ].style = style; metric_cache[ row ][ max ].c = c; metric_cache[ row ][ max ].time = current_time; *x_off = metric_cache[ row ][ max ].x_off = - ( ftf->glyph->advance.x + 0x20 ) >> 6; + ( font->face->glyph->advance.x + 0x20 ) >> 6; *y_off = metric_cache[ row ][ max ].y_off = - ( ftf->glyph->advance.y + 0x20 ) >> 6; + ( font->face->glyph->advance.y + 0x20 ) >> 6; } -static int query_glyph( uint32_t c ) { +static int query_glyph( enum style_id style, uint32_t c ) { int row = c & ( GLYPH_CACHE_SIZE - 1 ); int i; for( i = 0; i < GLYPH_CACHE_ASSOC; i++ ) - if( glyph_cache[ row ][ i ].c == c ) { + if( glyph_cache[ row ][ i ].style == style && + glyph_cache[ row ][ i ].c == c ) { /* Cache hit. */ glyph_cache[ row ][ i ].time = current_time; return ( row << GLYPH_CACHE_ASSOC_BITS ) | i; @@ -137,14 +264,15 @@ static int query_glyph( uint32_t c ) { return -1; } -static int replace_glyph( uint32_t c ) { +static int replace_glyph( enum style_id style, uint32_t c ) { int row = c & ( GLYPH_CACHE_SIZE - 1 ); int i, max; /* Search for a line to evict. */ for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ ) { - assert( glyph_cache[ row ][ i ].c != c ); + assert( glyph_cache[ row ][ i ].style != style || + glyph_cache[ row ][ i ].c != c); if( current_time - glyph_cache[ row ][ i ].time > current_time - glyph_cache[ row ][ max ].time ) max = i; @@ -154,13 +282,14 @@ static int replace_glyph( uint32_t c ) { /* Cache line set is full, and nothing is old enough to evict. */ return -1; + glyph_cache[ row ][ max ].style = style; glyph_cache[ row ][ max ].c = c; glyph_cache[ row ][ max ].time = current_time; return ( row << GLYPH_CACHE_ASSOC_BITS ) | max; } -static xcb_render_glyphset_t glyphset; +static xcb_render_glyphset_t glyphset; static struct picture { xcb_render_picture_t pic; enum decoration_col col; @@ -168,7 +297,7 @@ static struct picture { static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, int col, int x, int y, - const char *text, + const char *text, enum style_id style, const xcb_rectangle_t *clip ) { xcb_render_picture_t src, dest; @@ -318,19 +447,23 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, while( i < len && num_chars < 0xFF ) { int dx, dy, index; - if( ( index = query_glyph( buf[ i ] ) ) < 0 ) { + if( ( index = query_glyph( style, buf[ i ] ) ) < 0 ) { /* Cache miss. */ int bitmap_size; + struct font *font; FT_GlyphSlot slot; - if( ( index = replace_glyph( buf[ i ] ) ) < 0 ) + if( ( index = replace_glyph( style, buf[ i ] ) ) < 0 ) /* Cache set full: spill the partial string to make room for later glyphs. */ break; - if( FT_Load_Char( ftf, buf[ i ], FT_LOAD_RENDER ) || - ( bitmap_size = ( ( ftf->glyph->bitmap.width + 3 ) & ~3 ) * - ftf->glyph->bitmap.rows ) > sizeof data ) { + if( !( font = lookup_font( style, buf[ i ] ) ) || + FT_Load_Char( font->face, buf[ i ], font->load_flags | + FT_LOAD_RENDER ) || + ( bitmap_size = ( ( font->face->glyph->bitmap.width + + 3 ) & ~3 ) * + font->face->glyph->bitmap.rows ) > sizeof data ) { /* We couldn't load the character, or it was so huge that it won't fit in an empty AddGlyph data buffer. We'll have to send the server @@ -338,7 +471,7 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, slot = NULL; bitmap_size = 0; } else - slot = ftf->glyph; + slot = font->face->glyph; if( ( p - data ) + bitmap_size > sizeof data || num_glyphs == sizeof glyphids / sizeof *glyphids ) { @@ -355,10 +488,13 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, glyphids[ num_glyphs ] = index; if( slot ) { - int y, width_pad; - - glyphs[ num_glyphs ].width = slot->bitmap.width; - glyphs[ num_glyphs ].height = slot->bitmap.rows; + FT_Bitmap bitmap; + + FT_Bitmap_New( &bitmap ); + FT_Bitmap_Convert( ftl, &slot->bitmap, &bitmap, 4 ); + + glyphs[ num_glyphs ].width = bitmap.width; + glyphs[ num_glyphs ].height = bitmap.rows; glyphs[ num_glyphs ].x = -slot->bitmap_left; glyphs[ num_glyphs ].y = slot->bitmap_top; glyphs[ num_glyphs ].x_off = @@ -366,13 +502,20 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, glyphs[ num_glyphs ].y_off = ( slot->advance.y + 0x20 ) >> 6; - width_pad = ( slot->bitmap.width + 3 ) & ~3; + memcpy( p, bitmap.buffer, bitmap.pitch * bitmap.rows ); - for( y = 0; y < slot->bitmap.rows; y++ ) { - memcpy( p, slot->bitmap.buffer + y * - slot->bitmap.width, slot->bitmap.width ); - p += width_pad; + if( bitmap.num_grays != 0x100 ) { + unsigned char *c, + *end = p + bitmap.pitch * bitmap.rows; + + for( c = p; c < end; c++ ) + *c = ( (unsigned int) *c * 0xFF / + ( bitmap.num_grays - 1 ) ); } + + p += bitmap.pitch * bitmap.rows; + + FT_Bitmap_Done( ftl, &bitmap ); } else { glyphs[ num_glyphs ].width = 0; glyphs[ num_glyphs ].height = 0; @@ -388,7 +531,7 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, *( (uint16_t *) out ) = index; out += 2; - query_metrics( buf[ i ], &dx, &dy ); + query_metrics( style, buf[ i ], &dx, &dy ); x += dx; y += dy; @@ -426,8 +569,8 @@ extern void render_update_window( struct gwm_window *window ) { render_text( window->w, window->screen, window == focus_frame ? COL_TITLE_ACTIVE : COL_TITLE_INACTIVE, FRAME_BUTTON_SIZE + FRAME_BORDER_WIDTH * 3, - FONT_SIZE, name ? name : "(Untitled)", - &window->update ); + TITLE_FONT_SIZE, name ? name : "(Untitled)", + STYLE_TITLE, &window->update ); } else if( window->type == WINDOW_FEEDBACK ) { char *p, text[ 32 ]; int width; @@ -438,13 +581,13 @@ extern void render_update_window( struct gwm_window *window ) { for( width = 0, p = text; *p; p++ ) { int dx, dy; - query_metrics( *p, &dx, &dy ); + query_metrics( STYLE_TITLE, *p, &dx, &dy ); width += dx; } render_text( window->w, window->screen, COL_FEEDBACK_FORE, ( FEEDBACK_WIDTH - width ) >> 1, 16, text, - &window->update ); + STYLE_TITLE, &window->update ); } } @@ -618,50 +761,85 @@ static void decorate_compat_init( void ) { extern void decorate_render_init( void ) { - FcPattern *pat, *font; - FcResult r; - FcValue v; FT_Error err; - char *filename; - int i, index; + FcFontSet *sets[ NUM_STYLES ]; + FcPattern *style_pats[ NUM_STYLES ]; + FcChar32 *hashes; + FcResult r; + int i, total; xcb_pixmap_t pixmap; uint32_t n; - FcInit(); + if( !FcInit() ) + fatal( "font configuration error" ); - pat = FcNameParse( (FcChar8 *) FONT_NAME ); + if( ( err = FT_Init_FreeType( &ftl ) ) ) + fatal( "typeface initialisation error %d", err ); + + for( i = 0, total = 0; i < NUM_STYLES; i++ ) { + if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) ) + fatal( "could not parse font \"%s\"\n", style_names[ i ] ); - FcConfigSubstitute( NULL, pat, FcMatchPattern ); + FcConfigSubstitute( NULL, style_pats[ i ], FcMatchPattern ); + FcDefaultSubstitute( style_pats[ i ] ); - FcDefaultSubstitute( pat ); + sets[ i ] = FcFontSort( NULL, style_pats[ i ], TRUE, + &styles[ i ].charset, &r ); - font = FcFontMatch( NULL, pat, &r ); + total += sets[ i ]->nfont; + } - FcPatternGet( font, FC_FILE, 0, &v ); - filename = (char *) v.u.s; - FcPatternGet( font, FC_INDEX, 0, &v ); - index = v.u.i; + fonts = xmalloc( total * sizeof *fonts ); + hashes = alloca( total * sizeof *hashes ); + + for( i = 0; i < NUM_STYLES; i++ ) { + int j; - if( ( err = FT_Init_FreeType( &ftl ) ) ) - fatal( "typeface initialisation error %d", err ); + styles[ i ].fonts = xmalloc( sets[ i ]->nfont * + sizeof (struct font *) ); + + for( j = 0; j < sets[ i ]->nfont; j++ ) { + FcPattern *pat; + + if( ( pat = FcFontRenderPrepare( NULL, style_pats[ i ], + sets[ i ]->fonts[ j ] ) ) ) { + FcChar32 hash = FcPatternHash( pat ); + int search; + + for( search = 0; search < num_fonts; search++ ) + if( hashes[ search ] == hash && + FcPatternEqual( fonts[ search ].pattern, pat ) ) { + FcPatternDestroy( pat ); + break; + } - if( ( err = FT_New_Face( ftl, filename, index, &ftf ) ) ) - fatal( "%s: could not load font (%d)", filename, err ); + if( search == num_fonts ) { + hashes[ num_fonts ] = hash; + fonts[ num_fonts ].pattern = pat; + FcPatternGetCharSet( pat, FC_CHARSET, 0, + &fonts[ num_fonts ].charset ); + fonts[ num_fonts ].face = NULL; + num_fonts++; + } - FcPatternDestroy( pat ); - FcPatternDestroy( font ); + styles[ i ].fonts[ styles[ i ].num_fonts ] = search; + styles[ i ].num_fonts++; + } + } - FcFini(); + styles[ i ].fonts = xrealloc( styles[ i ].fonts, + styles[ i ].num_fonts * + sizeof (struct font *) ); - if( ( err = FT_Set_Char_Size( ftf, 0, FONT_SIZE << 6, 0, 0 ) ) ) - fatal( "%s: could not scale font (%d)", filename, err ); + FcFontSetDestroy( sets[ i ] ); + FcPatternDestroy( style_pats[ i ] ); + } - if( ( err = FT_Select_Charmap( ftf, FT_ENCODING_UNICODE ) ) ) - fatal( "%s: could not select character map (%d)", filename, err ); + fonts = xrealloc( fonts, num_fonts * sizeof *fonts ); glyphset = xcb_generate_id( c ); xcb_render_create_glyph_set( c, glyphset, fmt_a8 ); - + pixmap = xcb_generate_id( c ); pictures = xmalloc( num_screens * sizeof *pictures ); @@ -687,8 +865,24 @@ extern void decorate_render_init( void ) { extern void decorate_render_done( void ) { #if DEBUG - free( pictures ); - FT_Done_Face( ftf ); + int i; + + for( i = 0; i < NUM_STYLES; i++ ) { + FcCharSetDestroy( styles[ i ].charset ); + free( styles[ i ].fonts ); + } + + for( i = 0; i < num_fonts; i++ ) { + FcPatternDestroy( fonts[ i ].pattern ); + FT_Done_Face( fonts[ i ].face ); + } + + free( fonts ); + + FcFini(); + FT_Done_FreeType( ftl ); + + free( pictures ); #endif } |