summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Wong <gtw@gnu.org>2009-08-30 21:30:19 -0600
committerGary Wong <gtw@gnu.org>2009-08-30 21:30:19 -0600
commit20e28ea41bc3deae601e5995937c22d5d968cccd (patch)
tree95ac893cb04081ef1b9d8407950cf46b5cb2ba11
parentf62ba0d2b29be17191f17c8847036febb71aa79e (diff)
Add multiple text styles, and multiple faces per style.
-rw-r--r--ChangeLog10
-rw-r--r--decorate-render.c326
2 files changed, 270 insertions, 66 deletions
diff --git a/ChangeLog b/ChangeLog
index fe10886..066b10a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
}