1 /***************************************************************************/ 2 /* */ 3 /* ftccmap.c */ 4 /* */ 5 /* FreeType CharMap cache (body) */ 6 /* */ 7 /* Copyright 2000-2014 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 "ftcmanag.h" 23 #include FT_INTERNAL_MEMORY_H 24 #include FT_INTERNAL_OBJECTS_H 25 #include FT_INTERNAL_DEBUG_H 26 27 #include "ftccback.h" 28 #include "ftcerror.h" 29 30 #undef FT_COMPONENT 31 #define FT_COMPONENT trace_cache 32 33 34 /*************************************************************************/ 35 /* */ 36 /* Each FTC_CMapNode contains a simple array to map a range of character */ 37 /* codes to equivalent glyph indices. */ 38 /* */ 39 /* For now, the implementation is very basic: Each node maps a range of */ 40 /* 128 consecutive character codes to their corresponding glyph indices. */ 41 /* */ 42 /* We could do more complex things, but I don't think it is really very */ 43 /* useful. */ 44 /* */ 45 /*************************************************************************/ 46 47 48 /* number of glyph indices / character code per node */ 49 #define FTC_CMAP_INDICES_MAX 128 50 51 /* compute a query/node hash */ 52 #define FTC_CMAP_HASH( faceid, index, charcode ) \ 53 ( _FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \ 54 ( (charcode) / FTC_CMAP_INDICES_MAX ) ) 55 56 /* the charmap query */ 57 typedef struct FTC_CMapQueryRec_ 58 { 59 FTC_FaceID face_id; 60 FT_UInt cmap_index; 61 FT_UInt32 char_code; 62 63 } FTC_CMapQueryRec, *FTC_CMapQuery; 64 65 #define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x)) 66 #define FTC_CMAP_QUERY_HASH( x ) \ 67 FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->char_code ) 68 69 /* the cmap cache node */ 70 typedef struct FTC_CMapNodeRec_ 71 { 72 FTC_NodeRec node; 73 FTC_FaceID face_id; 74 FT_UInt cmap_index; 75 FT_UInt32 first; /* first character in node */ 76 FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */ 77 78 } FTC_CMapNodeRec, *FTC_CMapNode; 79 80 #define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) ) 81 #define FTC_CMAP_NODE_HASH( x ) \ 82 FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->first ) 83 84 /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */ 85 /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */ 86 #define FTC_CMAP_UNKNOWN (FT_UInt16)~0 87 88 89 /*************************************************************************/ 90 /*************************************************************************/ 91 /***** *****/ 92 /***** CHARMAP NODES *****/ 93 /***** *****/ 94 /*************************************************************************/ 95 /*************************************************************************/ 96 97 98 FT_CALLBACK_DEF( void ) ftc_cmap_node_free(FTC_Node ftcnode,FTC_Cache cache)99 ftc_cmap_node_free( FTC_Node ftcnode, 100 FTC_Cache cache ) 101 { 102 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 103 FT_Memory memory = cache->memory; 104 105 106 FT_FREE( node ); 107 } 108 109 110 /* initialize a new cmap node */ 111 FT_CALLBACK_DEF( FT_Error ) ftc_cmap_node_new(FTC_Node * ftcanode,FT_Pointer ftcquery,FTC_Cache cache)112 ftc_cmap_node_new( FTC_Node *ftcanode, 113 FT_Pointer ftcquery, 114 FTC_Cache cache ) 115 { 116 FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode; 117 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 118 FT_Error error; 119 FT_Memory memory = cache->memory; 120 FTC_CMapNode node = NULL; 121 FT_UInt nn; 122 123 124 if ( !FT_NEW( node ) ) 125 { 126 node->face_id = query->face_id; 127 node->cmap_index = query->cmap_index; 128 node->first = (query->char_code / FTC_CMAP_INDICES_MAX) * 129 FTC_CMAP_INDICES_MAX; 130 131 for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ ) 132 node->indices[nn] = FTC_CMAP_UNKNOWN; 133 } 134 135 *anode = node; 136 return error; 137 } 138 139 140 /* compute the weight of a given cmap node */ 141 FT_CALLBACK_DEF( FT_Offset ) ftc_cmap_node_weight(FTC_Node cnode,FTC_Cache cache)142 ftc_cmap_node_weight( FTC_Node cnode, 143 FTC_Cache cache ) 144 { 145 FT_UNUSED( cnode ); 146 FT_UNUSED( cache ); 147 148 return sizeof ( *cnode ); 149 } 150 151 152 /* compare a cmap node to a given query */ 153 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_node_compare(FTC_Node ftcnode,FT_Pointer ftcquery,FTC_Cache cache,FT_Bool * list_changed)154 ftc_cmap_node_compare( FTC_Node ftcnode, 155 FT_Pointer ftcquery, 156 FTC_Cache cache, 157 FT_Bool* list_changed ) 158 { 159 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 160 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 161 FT_UNUSED( cache ); 162 163 164 if ( list_changed ) 165 *list_changed = FALSE; 166 if ( node->face_id == query->face_id && 167 node->cmap_index == query->cmap_index ) 168 { 169 FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first ); 170 171 172 return FT_BOOL( offset < FTC_CMAP_INDICES_MAX ); 173 } 174 175 return 0; 176 } 177 178 179 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_node_remove_faceid(FTC_Node ftcnode,FT_Pointer ftcface_id,FTC_Cache cache,FT_Bool * list_changed)180 ftc_cmap_node_remove_faceid( FTC_Node ftcnode, 181 FT_Pointer ftcface_id, 182 FTC_Cache cache, 183 FT_Bool* list_changed ) 184 { 185 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 186 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 187 FT_UNUSED( cache ); 188 189 190 if ( list_changed ) 191 *list_changed = FALSE; 192 return FT_BOOL( node->face_id == face_id ); 193 } 194 195 196 /*************************************************************************/ 197 /*************************************************************************/ 198 /***** *****/ 199 /***** GLYPH IMAGE CACHE *****/ 200 /***** *****/ 201 /*************************************************************************/ 202 /*************************************************************************/ 203 204 205 static 206 const FTC_CacheClassRec ftc_cmap_cache_class = 207 { 208 ftc_cmap_node_new, 209 ftc_cmap_node_weight, 210 ftc_cmap_node_compare, 211 ftc_cmap_node_remove_faceid, 212 ftc_cmap_node_free, 213 214 sizeof ( FTC_CacheRec ), 215 ftc_cache_init, 216 ftc_cache_done, 217 }; 218 219 220 /* documentation is in ftcache.h */ 221 222 FT_EXPORT_DEF( FT_Error ) FTC_CMapCache_New(FTC_Manager manager,FTC_CMapCache * acache)223 FTC_CMapCache_New( FTC_Manager manager, 224 FTC_CMapCache *acache ) 225 { 226 return FTC_Manager_RegisterCache( manager, 227 &ftc_cmap_cache_class, 228 FTC_CACHE_P( acache ) ); 229 } 230 231 232 /* documentation is in ftcache.h */ 233 234 FT_EXPORT_DEF( FT_UInt ) FTC_CMapCache_Lookup(FTC_CMapCache cmap_cache,FTC_FaceID face_id,FT_Int cmap_index,FT_UInt32 char_code)235 FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache, 236 FTC_FaceID face_id, 237 FT_Int cmap_index, 238 FT_UInt32 char_code ) 239 { 240 FTC_Cache cache = FTC_CACHE( cmap_cache ); 241 FTC_CMapQueryRec query; 242 FTC_Node node; 243 FT_Error error; 244 FT_UInt gindex = 0; 245 FT_PtrDist hash; 246 FT_Int no_cmap_change = 0; 247 248 249 if ( cmap_index < 0 ) 250 { 251 /* Treat a negative cmap index as a special value, meaning that you */ 252 /* don't want to change the FT_Face's character map through this */ 253 /* call. This can be useful if the face requester callback already */ 254 /* sets the face's charmap to the appropriate value. */ 255 256 no_cmap_change = 1; 257 cmap_index = 0; 258 } 259 260 if ( !cache ) 261 { 262 FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" )); 263 return 0; 264 } 265 266 query.face_id = face_id; 267 query.cmap_index = (FT_UInt)cmap_index; 268 query.char_code = char_code; 269 270 hash = FTC_CMAP_HASH( face_id, cmap_index, char_code ); 271 272 #if 1 273 FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query, 274 node, error ); 275 #else 276 error = FTC_Cache_Lookup( cache, hash, &query, &node ); 277 #endif 278 if ( error ) 279 goto Exit; 280 281 FT_ASSERT( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first ) < 282 FTC_CMAP_INDICES_MAX ); 283 284 /* something rotten can happen with rogue clients */ 285 if ( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first >= 286 FTC_CMAP_INDICES_MAX ) ) 287 return 0; /* XXX: should return appropriate error */ 288 289 gindex = FTC_CMAP_NODE( node )->indices[char_code - 290 FTC_CMAP_NODE( node )->first]; 291 if ( gindex == FTC_CMAP_UNKNOWN ) 292 { 293 FT_Face face; 294 295 296 gindex = 0; 297 298 error = FTC_Manager_LookupFace( cache->manager, 299 FTC_CMAP_NODE( node )->face_id, 300 &face ); 301 if ( error ) 302 goto Exit; 303 304 #ifdef FT_MAX_CHARMAP_CACHEABLE 305 /* something rotten can happen with rogue clients */ 306 if ( cmap_index > FT_MAX_CHARMAP_CACHEABLE ) 307 return 0; /* XXX: should return appropriate error */ 308 #endif 309 310 if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps ) 311 { 312 FT_CharMap old, cmap = NULL; 313 314 315 old = face->charmap; 316 cmap = face->charmaps[cmap_index]; 317 318 if ( old != cmap && !no_cmap_change ) 319 FT_Set_Charmap( face, cmap ); 320 321 gindex = FT_Get_Char_Index( face, char_code ); 322 323 if ( old != cmap && !no_cmap_change ) 324 FT_Set_Charmap( face, old ); 325 } 326 327 FTC_CMAP_NODE( node )->indices[char_code - 328 FTC_CMAP_NODE( node )->first] 329 = (FT_UShort)gindex; 330 } 331 332 Exit: 333 return gindex; 334 } 335 336 337 /* END */ 338