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