1 /***************************************************************************/ 2 /* */ 3 /* ftccmap.c */ 4 /* */ 5 /* FreeType CharMap cache (body) */ 6 /* */ 7 /* Copyright 2000-2001, 2002 by */ 8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 9 /* */ 10 /* This file is part of the FreeType project, and may only be used, */ 11 /* modified, and distributed under the terms of the FreeType project */ 12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 13 /* this file you indicate that you have read the license and */ 14 /* understand and accept it fully. */ 15 /* */ 16 /***************************************************************************/ 17 18 19 #include <ft2build.h> 20 #include FT_FREETYPE_H 21 #include FT_CACHE_H 22 #include FT_CACHE_CHARMAP_H 23 #include FT_CACHE_MANAGER_H 24 #include FT_INTERNAL_MEMORY_H 25 #include FT_INTERNAL_DEBUG_H 26 27 #include "ftcerror.h" 28 29 /*************************************************************************/ 30 /* */ 31 /* Each FTC_CMapNode contains a simple array to map a range of character */ 32 /* codes to equivalent glyph indices. */ 33 /* */ 34 /* For now, the implementation is very basic: Each node maps a range of */ 35 /* 128 consecutive character codes to their corresponding glyph indices. */ 36 /* */ 37 /* We could do more complex things, but I don't think it is really very */ 38 /* useful. */ 39 /* */ 40 /*************************************************************************/ 41 42 43 /* number of glyph indices / character code per node */ 44 #define FTC_CMAP_INDICES_MAX 128 45 46 47 typedef struct FTC_CMapNodeRec_ 48 { 49 FTC_NodeRec node; 50 FT_UInt32 first; /* first character in node */ 51 FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */ 52 53 } FTC_CMapNodeRec, *FTC_CMapNode; 54 55 56 #define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) ) 57 58 59 /* compute node hash value from cmap family and "requested" glyph index */ 60 #define FTC_CMAP_HASH( cfam, cquery ) \ 61 ( (cfam)->hash + ( (cquery)->char_code / FTC_CMAP_INDICES_MAX ) ) 62 63 /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */ 64 /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */ 65 #define FTC_CMAP_UNKNOWN ( (FT_UInt16)-1 ) 66 67 68 /* the charmap query */ 69 typedef struct FTC_CMapQueryRec_ 70 { 71 FTC_QueryRec query; 72 FTC_CMapDesc desc; 73 FT_UInt32 char_code; 74 75 } FTC_CMapQueryRec, *FTC_CMapQuery; 76 77 78 #define FTC_CMAP_QUERY( x ) ( (FTC_CMapQuery)( x ) ) 79 80 81 /* the charmap family */ 82 typedef struct FTC_CMapFamilyRec_ 83 { 84 FTC_FamilyRec family; 85 FT_UInt32 hash; 86 FTC_CMapDescRec desc; 87 FT_UInt index; 88 89 } FTC_CMapFamilyRec, *FTC_CMapFamily; 90 91 92 #define FTC_CMAP_FAMILY( x ) ( (FTC_CMapFamily)( x ) ) 93 #define FTC_CMAP_FAMILY_MEMORY( x ) FTC_FAMILY( x )->memory 94 95 96 /*************************************************************************/ 97 /*************************************************************************/ 98 /***** *****/ 99 /***** CHARMAP NODES *****/ 100 /***** *****/ 101 /*************************************************************************/ 102 /*************************************************************************/ 103 104 105 /* no need for specific finalizer; we use "ftc_node_done" directly */ 106 107 /* initialize a new cmap node */ 108 FT_CALLBACK_DEF( FT_Error ) ftc_cmap_node_init(FTC_CMapNode cnode,FTC_CMapQuery cquery,FTC_Cache cache)109 ftc_cmap_node_init( FTC_CMapNode cnode, 110 FTC_CMapQuery cquery, 111 FTC_Cache cache ) 112 { 113 FT_UInt32 first; 114 FT_UInt n; 115 FT_UNUSED( cache ); 116 117 118 first = ( cquery->char_code / FTC_CMAP_INDICES_MAX ) * 119 FTC_CMAP_INDICES_MAX; 120 121 cnode->first = first; 122 for ( n = 0; n < FTC_CMAP_INDICES_MAX; n++ ) 123 cnode->indices[n] = FTC_CMAP_UNKNOWN; 124 125 return 0; 126 } 127 128 129 /* compute the weight of a given cmap node */ 130 FT_CALLBACK_DEF( FT_ULong ) ftc_cmap_node_weight(FTC_CMapNode cnode)131 ftc_cmap_node_weight( FTC_CMapNode cnode ) 132 { 133 FT_UNUSED( cnode ); 134 135 return sizeof ( *cnode ); 136 } 137 138 139 /* compare a cmap node to a given query */ 140 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_node_compare(FTC_CMapNode cnode,FTC_CMapQuery cquery)141 ftc_cmap_node_compare( FTC_CMapNode cnode, 142 FTC_CMapQuery cquery ) 143 { 144 FT_UInt32 offset = (FT_UInt32)( cquery->char_code - cnode->first ); 145 146 147 return FT_BOOL( offset < FTC_CMAP_INDICES_MAX ); 148 } 149 150 151 /*************************************************************************/ 152 /*************************************************************************/ 153 /***** *****/ 154 /***** CHARMAP FAMILY *****/ 155 /***** *****/ 156 /*************************************************************************/ 157 /*************************************************************************/ 158 159 160 FT_CALLBACK_DEF( FT_Error ) ftc_cmap_family_init(FTC_CMapFamily cfam,FTC_CMapQuery cquery,FTC_Cache cache)161 ftc_cmap_family_init( FTC_CMapFamily cfam, 162 FTC_CMapQuery cquery, 163 FTC_Cache cache ) 164 { 165 FTC_Manager manager = cache->manager; 166 FTC_CMapDesc desc = cquery->desc; 167 FT_UInt32 hash = 0; 168 FT_Error error; 169 FT_Face face; 170 171 172 /* setup charmap descriptor */ 173 cfam->desc = *desc; 174 175 /* let's see whether the rest is correct too */ 176 error = FTC_Manager_Lookup_Face( manager, desc->face_id, &face ); 177 if ( !error ) 178 { 179 FT_UInt count = face->num_charmaps; 180 FT_UInt idx = count; 181 FT_CharMap* cur = face->charmaps; 182 183 184 switch ( desc->type ) 185 { 186 case FTC_CMAP_BY_INDEX: 187 idx = desc->u.index; 188 hash = idx * 33; 189 break; 190 191 case FTC_CMAP_BY_ENCODING: 192 for ( idx = 0; idx < count; idx++, cur++ ) 193 if ( cur[0]->encoding == desc->u.encoding ) 194 break; 195 196 hash = idx * 67; 197 break; 198 199 case FTC_CMAP_BY_ID: 200 for ( idx = 0; idx < count; idx++, cur++ ) 201 { 202 if ( (FT_UInt)cur[0]->platform_id == desc->u.id.platform && 203 (FT_UInt)cur[0]->encoding_id == desc->u.id.encoding ) 204 { 205 hash = ( ( desc->u.id.platform << 8 ) | desc->u.id.encoding ) * 7; 206 break; 207 } 208 } 209 break; 210 211 default: 212 ; 213 } 214 215 if ( idx >= count ) 216 goto Bad_Descriptor; 217 218 /* compute hash value, both in family and query */ 219 cfam->index = idx; 220 cfam->hash = hash ^ FTC_FACE_ID_HASH( desc->face_id ); 221 FTC_QUERY( cquery )->hash = FTC_CMAP_HASH( cfam, cquery ); 222 223 error = ftc_family_init( FTC_FAMILY( cfam ), 224 FTC_QUERY( cquery ), cache ); 225 } 226 227 return error; 228 229 Bad_Descriptor: 230 FT_ERROR(( "ftp_cmap_family_init: invalid charmap descriptor\n" )); 231 return FTC_Err_Invalid_Argument; 232 } 233 234 235 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_family_compare(FTC_CMapFamily cfam,FTC_CMapQuery cquery)236 ftc_cmap_family_compare( FTC_CMapFamily cfam, 237 FTC_CMapQuery cquery ) 238 { 239 FT_Int result = 0; 240 241 242 /* first, compare face id and type */ 243 if ( cfam->desc.face_id != cquery->desc->face_id || 244 cfam->desc.type != cquery->desc->type ) 245 goto Exit; 246 247 switch ( cfam->desc.type ) 248 { 249 case FTC_CMAP_BY_INDEX: 250 result = ( cfam->desc.u.index == cquery->desc->u.index ); 251 break; 252 253 case FTC_CMAP_BY_ENCODING: 254 result = ( cfam->desc.u.encoding == cquery->desc->u.encoding ); 255 break; 256 257 case FTC_CMAP_BY_ID: 258 result = ( cfam->desc.u.id.platform == cquery->desc->u.id.platform && 259 cfam->desc.u.id.encoding == cquery->desc->u.id.encoding ); 260 break; 261 262 default: 263 ; 264 } 265 266 if ( result ) 267 { 268 /* when found, update the 'family' and 'hash' field of the query */ 269 FTC_QUERY( cquery )->family = FTC_FAMILY( cfam ); 270 FTC_QUERY( cquery )->hash = FTC_CMAP_HASH( cfam, cquery ); 271 } 272 273 Exit: 274 return FT_BOOL( result ); 275 } 276 277 278 /*************************************************************************/ 279 /*************************************************************************/ 280 /***** *****/ 281 /***** GLYPH IMAGE CACHE *****/ 282 /***** *****/ 283 /*************************************************************************/ 284 /*************************************************************************/ 285 286 287 FT_CALLBACK_TABLE_DEF 288 const FTC_Cache_ClassRec ftc_cmap_cache_class = 289 { 290 sizeof ( FTC_CacheRec ), 291 (FTC_Cache_InitFunc) ftc_cache_init, 292 (FTC_Cache_ClearFunc)ftc_cache_clear, 293 (FTC_Cache_DoneFunc) ftc_cache_done, 294 295 sizeof ( FTC_CMapFamilyRec ), 296 (FTC_Family_InitFunc) ftc_cmap_family_init, 297 (FTC_Family_CompareFunc)ftc_cmap_family_compare, 298 (FTC_Family_DoneFunc) ftc_family_done, 299 300 sizeof ( FTC_CMapNodeRec ), 301 (FTC_Node_InitFunc) ftc_cmap_node_init, 302 (FTC_Node_WeightFunc) ftc_cmap_node_weight, 303 (FTC_Node_CompareFunc)ftc_cmap_node_compare, 304 (FTC_Node_DoneFunc) ftc_node_done 305 }; 306 307 308 /* documentation is in ftccmap.h */ 309 310 FT_EXPORT_DEF( FT_Error ) FTC_CMapCache_New(FTC_Manager manager,FTC_CMapCache * acache)311 FTC_CMapCache_New( FTC_Manager manager, 312 FTC_CMapCache *acache ) 313 { 314 return FTC_Manager_Register_Cache( 315 manager, 316 (FTC_Cache_Class)&ftc_cmap_cache_class, 317 FTC_CACHE_P( acache ) ); 318 } 319 320 321 #ifdef FTC_CACHE_USE_INLINE 322 323 #define GEN_CACHE_FAMILY_COMPARE( f, q, c ) \ 324 ftc_cmap_family_compare( (FTC_CMapFamily)(f), (FTC_CMapQuery)(q) ) 325 326 #define GEN_CACHE_NODE_COMPARE( n, q, c ) \ 327 ftc_cmap_node_compare( (FTC_CMapNode)(n), (FTC_CMapQuery)(q) ) 328 329 #define GEN_CACHE_LOOKUP ftc_cmap_cache_lookup 330 331 #include "ftccache.i" 332 333 #else /* !FTC_CACHE_USE_INLINE */ 334 335 #define ftc_cmap_cache_lookup ftc_cache_lookup 336 337 #endif /* !FTC_CACHE_USE_INLINE */ 338 339 340 /* documentation is in ftccmap.h */ 341 342 FT_EXPORT_DEF( FT_UInt ) FTC_CMapCache_Lookup(FTC_CMapCache cache,FTC_CMapDesc desc,FT_UInt32 char_code)343 FTC_CMapCache_Lookup( FTC_CMapCache cache, 344 FTC_CMapDesc desc, 345 FT_UInt32 char_code ) 346 { 347 FTC_CMapQueryRec cquery; 348 FTC_CMapNode node; 349 FT_Error error; 350 FT_UInt gindex = 0; 351 352 353 if ( !cache || !desc ) 354 { 355 FT_ERROR(( "FTC_CMapCache_Lookup: bad arguments, returning 0!\n" )); 356 return 0; 357 } 358 359 cquery.desc = desc; 360 cquery.char_code = char_code; 361 362 error = ftc_cmap_cache_lookup( FTC_CACHE( cache ), 363 FTC_QUERY( &cquery ), 364 (FTC_Node*)&node ); 365 if ( !error ) 366 { 367 FT_UInt offset = (FT_UInt)( char_code - node->first ); 368 369 370 FT_ASSERT( offset < FTC_CMAP_INDICES_MAX ); 371 372 gindex = node->indices[offset]; 373 if ( gindex == FTC_CMAP_UNKNOWN ) 374 { 375 FT_Face face; 376 377 378 /* we need to use FT_Get_Char_Index */ 379 gindex = 0; 380 381 error = FTC_Manager_Lookup_Face( FTC_CACHE(cache)->manager, 382 desc->face_id, 383 &face ); 384 if ( !error ) 385 { 386 FT_CharMap old, cmap = NULL; 387 FT_UInt cmap_index; 388 389 390 /* save old charmap, select new one */ 391 old = face->charmap; 392 cmap_index = FTC_CMAP_FAMILY( FTC_QUERY( &cquery )->family )->index; 393 cmap = face->charmaps[cmap_index]; 394 395 FT_Set_Charmap( face, cmap ); 396 397 /* perform lookup */ 398 gindex = FT_Get_Char_Index( face, char_code ); 399 node->indices[offset] = (FT_UInt16)gindex; 400 401 /* restore old charmap */ 402 FT_Set_Charmap( face, old ); 403 } 404 } 405 } 406 407 return gindex; 408 } 409 410 411 /* END */ 412