1/***************************************************************************/
2/*                                                                         */
3/*  ftccmap.c                                                              */
4/*                                                                         */
5/*    FreeType CharMap cache (body)                                        */
6/*                                                                         */
7/*  Copyright 2000-2013 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 )
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 )
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 )
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 )
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 )
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  FT_CALLBACK_TABLE_DEF
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 )
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 )
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