/* (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include D_DEBUG_DOMAIN( Core_Font, "Core/Font", "DirectFB Core Font" ); D_DEBUG_DOMAIN( Core_FontSurfaces, "Core/Font/Surf", "DirectFB Core Font Surfaces" ); /**********************************************************************************************************************/ static bool free_glyphs( DirectHash *hash, unsigned long key, void *value, void *ctx ); /**********************************************************************************************************************/ DFBResult dfb_font_create( CoreDFB *core, const DFBFontDescription *description, const char *url, CoreFont **ret_font ) { DFBResult ret; int i; CoreFont *font; D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_ASSERT( core != NULL ); D_ASSERT( ret_font != NULL ); font = D_CALLOC( 1, sizeof(CoreFont) ); if (!font) return D_OOM(); for (i=0; ilayers[i].glyph_hash ); if (ret) { while (i--) direct_hash_destroy( font->layers[i].glyph_hash ); D_FREE( font ); return ret; } } font->description = *description; font->url = D_STRDUP( url ); font->core = core; font->max_rows = 2; direct_util_recursive_pthread_mutex_init( &font->lock ); /* the proposed pixel_format, may be changed by the font provider */ font->pixel_format = dfb_config->font_format ? : DSPF_A8; if ((font->pixel_format == DSPF_ARGB || font->pixel_format == DSPF_ARGB4444) && dfb_config->font_premult) font->surface_caps = DSCAPS_PREMULTIPLIED; font->blittingflags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE; D_MAGIC_SET( font, CoreFont ); *ret_font = font; return DFB_OK; } void dfb_font_destroy( CoreFont *font ) { int i; D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( font, CoreFont ); D_MAGIC_CLEAR( font ); D_FREE( font->url ); pthread_mutex_lock( &font->lock ); for (i=0; ilayers[i].glyph_hash, free_glyphs, NULL ); direct_hash_destroy( font->layers[i].glyph_hash ); } if (font->rows) { for (i = 0; i < font->num_rows; i++) { CoreFontCacheRow *row = font->rows[i]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); dfb_surface_unref( row->surface ); D_MAGIC_CLEAR( row ); D_FREE( row ); } D_FREE( font->rows ); } D_ASSERT( font->encodings != NULL || !font->last_encoding ); for (i=DTEID_OTHER; i<=font->last_encoding; i++) { CoreFontEncoding *encoding = font->encodings[i]; D_ASSERT( encoding != NULL ); D_ASSERT( encoding->name != NULL ); D_MAGIC_CLEAR( encoding ); D_FREE( encoding->name ); D_FREE( encoding ); } if (font->encodings) D_FREE( font->encodings ); pthread_mutex_unlock( &font->lock ); pthread_mutex_destroy( &font->lock ); D_FREE( font ); } /**********************************************************************************************************************/ DFBResult dfb_font_get_glyph_data( CoreFont *font, unsigned int index, unsigned int layer, CoreGlyphData **ret_data ) { DFBResult ret; CoreGlyphData *data; int i; int align; CoreFontCacheRow *row = NULL; D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( font, CoreFont ); D_ASSERT( ret_data != NULL ); D_ASSERT( layer < D_ARRAY_SIZE(font->layers) ); D_ASSERT( font->num_rows >= 0 ); if (font->num_rows) { D_ASSERT( font->num_rows <= font->max_rows || font->max_rows < 0 ); D_ASSERT( font->active_row >= 0 ); D_ASSERT( font->active_row < font->num_rows ); } if (index < 128 && font->layers[layer].glyph_data[index]) { data = font->layers[layer].glyph_data[index]; if (data->failed) goto retry; *ret_data = font->layers[layer].glyph_data[index]; return DFB_OK; } data = direct_hash_lookup( font->layers[layer].glyph_hash, index ); if (data) { D_MAGIC_ASSERT( data, CoreGlyphData ); if (font->rows) { D_ASSERT( data->row >= 0 ); D_ASSERT( data->row < font->num_rows ); row = font->rows[data->row]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); row->stamp = font->row_stamp++; } if (data->failed) goto retry; *ret_data = data; return DFB_OK; } if (!font->GetGlyphData) return DFB_UNSUPPORTED; data = D_CALLOC( 1, sizeof(CoreGlyphData) ); if (!data) return D_OOM(); D_MAGIC_SET( data, CoreGlyphData ); data->index = index; data->layer = layer; retry: data->failed = false; ret = font->GetGlyphData( font, index, data ); if (ret) { D_DERROR( ret, "Core/Font: Could not get glyph info for index %d!\n", index ); data->start = data->width = data->height = 0; data->failed = true; goto out; } if (data->width < 1 || data->height < 1) { data->start = data->width = data->height = 0; goto out; } if (font->rows) { D_ASSERT( font->active_row >= 0 ); D_ASSERT( font->active_row < font->num_rows ); row = font->rows[font->active_row]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); } else { /* Calculate row width? */ if (font->row_width == 0) { int width = 2048 * font->height / 64; if (width > 2048) width = 2048; if (width < font->maxadvance) width = font->maxadvance; font->row_width = (width + 7) & ~7; } } /* Need another font surface? */ if (!row || (row->next_x + data->width > font->row_width)) { D_ASSERT( font->max_rows != 0 ); /* Maximum number of rows reached? */ if (font->num_rows == font->max_rows) { int best_row = -1; unsigned int best_val = 0; /* Check for trailing space first. */ for (i=0; inum_rows; i++) { row = font->rows[i]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); if (row->next_x + data->width <= font->row_width) { if (best_row == -1 || best_val < row->next_x) { best_row = i; best_val = row->next_x; } } } /* Found a row with enough trailing space? */ if (best_row != -1) { font->active_row = best_row; row = font->rows[best_row]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); D_DEBUG_AT( Core_FontSurfaces, " -> using trailing space of row %d - %dx%d %s\n", font->active_row, row->surface->config.size.w, row->surface->config.size.h, dfb_pixelformat_name(row->surface->config.format) ); } else { CoreGlyphData *d, *n; D_ASSERT( best_row == -1 ); D_ASSERT( best_val == 0 ); /* Reuse the least recently used row. */ for (i=0; inum_rows; i++) { row = font->rows[i]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); if (best_row == -1 || best_val > row->stamp) { best_row = i; best_val = row->stamp; } } D_ASSERT( best_row != -1 ); font->active_row = best_row; row = font->rows[best_row]; D_MAGIC_ASSERT( row, CoreFontCacheRow ); D_DEBUG_AT( Core_FontSurfaces, " -> reusing row %d - %dx%d %s\n", font->active_row, row->surface->config.size.w, row->surface->config.size.h, dfb_pixelformat_name(row->surface->config.format) ); /* Kick out all glyphs. */ direct_list_foreach_safe (d, n, row->glyphs) { D_MAGIC_ASSERT( d, CoreGlyphData ); D_ASSERT( d->layer < D_ARRAY_SIZE(font->layers) ); /*ret =*/ direct_hash_remove( font->layers[d->layer].glyph_hash, d->index ); //FIXME: use D_ASSERT( ret == DFB_OK ); if (d->index < 128) font->layers[d->layer].glyph_data[d->index] = NULL; D_MAGIC_CLEAR( d ); D_FREE( d ); } /* Reset row. */ row->glyphs = NULL; row->next_x = 0; } } else { /* Allocate new font cache row structure. */ row = D_CALLOC( 1, sizeof(CoreFontCacheRow) ); if (!row) { ret = D_OOM(); goto error; } /* Create a new font surface. */ ret = dfb_surface_create_simple( font->core, font->row_width, MAX( font->height + 1, 8 ), font->pixel_format, font->surface_caps, CSTF_FONT, 0 /* FIXME: no shared fonts, no font id */, NULL, &row->surface ); if (ret) { D_DERROR( ret, "Core/Font: Could not create font surface!\n" ); D_FREE( row ); goto error; } D_DEBUG_AT( Core_FontSurfaces, " -> new row %d - %dx%d %s\n", font->num_rows, row->surface->config.size.w, row->surface->config.size.h, dfb_pixelformat_name(row->surface->config.format) ); D_MAGIC_SET( row, CoreFontCacheRow ); /* Append to array. FIXME: Use vector to avoid realloc each time! */ font->rows = D_REALLOC( font->rows, sizeof(void*) * (font->num_rows + 1) ); D_ASSERT( font->rows != NULL ); font->rows[font->num_rows] = row; /* Set new row to use. */ font->active_row = font->num_rows++; } } D_MAGIC_ASSERT( row, CoreFontCacheRow ); D_ASSERT( font->num_rows > 0 ); D_ASSERT( font->num_rows <= font->max_rows || font->max_rows < 0 ); D_ASSERT( font->active_row >= 0 ); D_ASSERT( font->active_row < font->num_rows ); D_ASSERT( row == font->rows[font->active_row] ); D_DEBUG_AT( Core_FontSurfaces, " -> render %2d - %2dx%2d at %d:%03d font <%p>\n", index, data->width, data->height, font->active_row, row->next_x, font ); data->row = font->active_row; data->start = row->next_x; data->surface = row->surface; align = (8 / (DFB_BYTES_PER_PIXEL( font->pixel_format ) ? : 1)) * (DFB_PIXELFORMAT_ALIGNMENT( font->pixel_format ) + 1) - 1; row->next_x += (data->width + align) & ~align; row->stamp = font->row_stamp++; /* Render the glyph data into the surface. */ ret = font->RenderGlyph( font, index, data ); if (ret) { data->start = data->width = data->height = 0; data->failed = true; goto out; } dfb_gfxcard_flush_texture_cache(); out: if (!data->inserted) { if (row) direct_list_append( &row->glyphs, &data->link ); direct_hash_insert( font->layers[layer].glyph_hash, index, data ); if (index < 128) font->layers[layer].glyph_data[index] = data; data->inserted = true; } *ret_data = data; return DFB_OK; error: D_MAGIC_CLEAR( data ); D_FREE( data ); return ret; } /**********************************************************************************************************************/ DFBResult dfb_font_register_encoding( CoreFont *font, const char *name, const CoreFontEncodingFuncs *funcs, DFBTextEncodingID encoding_id ) { CoreFontEncoding *encoding; CoreFontEncoding **encodings; D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( font, CoreFont ); D_ASSERT( encoding_id == DTEID_UTF8 || name != NULL ); D_ASSERT( funcs != NULL ); if (!funcs->GetCharacterIndex) return DFB_INVARG; /* Special case for default, native format. */ if (encoding_id == DTEID_UTF8) { font->utf8 = funcs; return DFB_OK; } if (!funcs->DecodeText) return DFB_INVARG; /* Setup new encoding information. */ encoding = D_CALLOC( 1, sizeof(CoreFontEncoding) ); if (!encoding) return D_OOM(); encoding->encoding = font->last_encoding + 1; encoding->funcs = funcs; encoding->name = D_STRDUP( name ); if (!encoding->name) { D_FREE( encoding ); return D_OOM(); } /* Add to array. */ encodings = D_REALLOC( font->encodings, (encoding->encoding + 1) * sizeof(CoreFontEncoding*) ); if (!encodings) { D_FREE( encoding->name ); D_FREE( encoding ); return D_OOM(); } font->encodings = encodings; font->last_encoding++; D_ASSERT( font->last_encoding == encoding->encoding ); encodings[encoding->encoding] = encoding; D_MAGIC_SET( encoding, CoreFontEncoding ); return DFB_OK; } DFBResult dfb_font_decode_text( CoreFont *font, DFBTextEncodingID encoding, const void *text, int length, unsigned int *ret_indices, int *ret_num ) { int pos = 0, num = 0; const u8 *bytes = text; D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( font, CoreFont ); D_ASSERT( text != NULL ); D_ASSERT( length >= 0 ); /* TODO: handle -1 here? */ D_ASSERT( ret_indices != NULL ); D_ASSERT( ret_num != NULL ); if (encoding != DTEID_UTF8) { if (encoding > font->last_encoding) return DFB_IDNOTFOUND; D_ASSERT( font->encodings[encoding] != NULL ); D_ASSERT( font->encodings[encoding]->funcs != NULL ); D_ASSERT( font->encodings[encoding]->funcs->DecodeText != NULL ); return font->encodings[encoding]->funcs->DecodeText( font, text, length, ret_indices, ret_num ); } else if (font->utf8) { const CoreFontEncodingFuncs *funcs = font->utf8; if (funcs->DecodeText) return funcs->DecodeText( font, text, length, ret_indices, ret_num ); D_ASSERT( funcs->GetCharacterIndex != NULL ); while (pos < length) { unsigned int c; if (bytes[pos] < 128) c = bytes[pos++]; else { c = DIRECT_UTF8_GET_CHAR( &bytes[pos] ); pos += DIRECT_UTF8_SKIP(bytes[pos]); } if (funcs->GetCharacterIndex( font, c, &ret_indices[num] ) == DFB_OK) num++; } } else { while (pos < length) { if (bytes[pos] < 128) ret_indices[num++] = bytes[pos++]; else { ret_indices[num++] = DIRECT_UTF8_GET_CHAR( &bytes[pos] ); pos += DIRECT_UTF8_SKIP(bytes[pos]); } } } *ret_num = num; return DFB_OK; } DFBResult dfb_font_decode_character( CoreFont *font, DFBTextEncodingID encoding, u32 character, unsigned int *ret_index ) { D_DEBUG_AT( Core_Font, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( font, CoreFont ); D_ASSERT( ret_index != NULL ); if (encoding > font->last_encoding) return DFB_IDNOTFOUND; if (encoding != DTEID_UTF8) { D_ASSERT( font->encodings[encoding] != NULL ); D_ASSERT( font->encodings[encoding]->funcs != NULL ); D_ASSERT( font->encodings[encoding]->funcs->GetCharacterIndex != NULL ); return font->encodings[encoding]->funcs->GetCharacterIndex( font, character, ret_index ); } else if (font->utf8) { const CoreFontEncodingFuncs *funcs = font->utf8; D_ASSERT( funcs->GetCharacterIndex != NULL ); return funcs->GetCharacterIndex( font, character, ret_index ); } else *ret_index = character; return DFB_OK; } /**********************************************************************************************************************/ static bool free_glyphs( DirectHash *hash, unsigned long key, void *value, void *ctx ) { CoreGlyphData *data = value; D_MAGIC_ASSERT( data, CoreGlyphData ); D_MAGIC_CLEAR( data ); D_FREE( data ); return true; }