1 /****************************************************************************
2  *
3  * ftcbasic.c
4  *
5  *   The FreeType basic cache interface (body).
6  *
7  * Copyright (C) 2003-2021 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 <freetype/internal/ftobjs.h>
20 #include <freetype/internal/ftdebug.h>
21 #include <freetype/ftcache.h>
22 #include "ftcglyph.h"
23 #include "ftcimage.h"
24 #include "ftcsbits.h"
25 
26 #include "ftccback.h"
27 #include "ftcerror.h"
28 
29 #define FT_COMPONENT  cache
30 
31 
32   /*
33    * Basic Families
34    *
35    */
36   typedef struct  FTC_BasicAttrRec_
37   {
38     FTC_ScalerRec  scaler;
39     FT_UInt        load_flags;
40 
41   } FTC_BasicAttrRec, *FTC_BasicAttrs;
42 
43 #define FTC_BASIC_ATTR_COMPARE( a, b )                                 \
44           FT_BOOL( FTC_SCALER_COMPARE( &(a)->scaler, &(b)->scaler ) && \
45                    (a)->load_flags == (b)->load_flags               )
46 
47 #define FTC_BASIC_ATTR_HASH( a )                                     \
48           ( FTC_SCALER_HASH( &(a)->scaler ) + 31 * (a)->load_flags )
49 
50 
51   typedef struct  FTC_BasicQueryRec_
52   {
53     FTC_GQueryRec     gquery;
54     FTC_BasicAttrRec  attrs;
55 
56   } FTC_BasicQueryRec, *FTC_BasicQuery;
57 
58 
59   typedef struct  FTC_BasicFamilyRec_
60   {
61     FTC_FamilyRec     family;
62     FTC_BasicAttrRec  attrs;
63 
64   } FTC_BasicFamilyRec, *FTC_BasicFamily;
65 
66 
67   FT_CALLBACK_DEF( FT_Bool )
ftc_basic_family_compare(FTC_MruNode ftcfamily,FT_Pointer ftcquery)68   ftc_basic_family_compare( FTC_MruNode  ftcfamily,
69                             FT_Pointer   ftcquery )
70   {
71     FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
72     FTC_BasicQuery   query  = (FTC_BasicQuery)ftcquery;
73 
74 
75     return FTC_BASIC_ATTR_COMPARE( &family->attrs, &query->attrs );
76   }
77 
78 
79   FT_CALLBACK_DEF( FT_Error )
ftc_basic_family_init(FTC_MruNode ftcfamily,FT_Pointer ftcquery,FT_Pointer ftccache)80   ftc_basic_family_init( FTC_MruNode  ftcfamily,
81                          FT_Pointer   ftcquery,
82                          FT_Pointer   ftccache )
83   {
84     FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
85     FTC_BasicQuery   query  = (FTC_BasicQuery)ftcquery;
86     FTC_Cache        cache  = (FTC_Cache)ftccache;
87 
88 
89     FTC_Family_Init( FTC_FAMILY( family ), cache );
90     family->attrs = query->attrs;
91     return 0;
92   }
93 
94 
95   FT_CALLBACK_DEF( FT_UInt )
ftc_basic_family_get_count(FTC_Family ftcfamily,FTC_Manager manager)96   ftc_basic_family_get_count( FTC_Family   ftcfamily,
97                               FTC_Manager  manager )
98   {
99     FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
100     FT_Error         error;
101     FT_Face          face;
102     FT_UInt          result = 0;
103 
104 
105     error = FTC_Manager_LookupFace( manager, family->attrs.scaler.face_id,
106                                     &face );
107 
108     if ( error || !face )
109       return result;
110 
111 #ifdef FT_DEBUG_LEVEL_TRACE
112     if ( (FT_ULong)face->num_glyphs > FT_UINT_MAX || 0 > face->num_glyphs )
113     {
114       FT_TRACE1(( "ftc_basic_family_get_count:"
115                   " the number of glyphs in this face is %ld,\n",
116                   face->num_glyphs ));
117       FT_TRACE1(( "                           "
118                   " which is too much and thus truncated\n" ));
119     }
120 #endif
121 
122     if ( !error )
123       result = (FT_UInt)face->num_glyphs;
124 
125     return result;
126   }
127 
128 
129   FT_CALLBACK_DEF( FT_Error )
ftc_basic_family_load_bitmap(FTC_Family ftcfamily,FT_UInt gindex,FTC_Manager manager,FT_Face * aface)130   ftc_basic_family_load_bitmap( FTC_Family   ftcfamily,
131                                 FT_UInt      gindex,
132                                 FTC_Manager  manager,
133                                 FT_Face     *aface )
134   {
135     FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
136     FT_Error         error;
137     FT_Size          size;
138 
139 
140     error = FTC_Manager_LookupSize( manager, &family->attrs.scaler, &size );
141     if ( !error )
142     {
143       FT_Face  face = size->face;
144 
145 
146       error = FT_Load_Glyph(
147                 face,
148                 gindex,
149                 (FT_Int)family->attrs.load_flags | FT_LOAD_RENDER );
150       if ( !error )
151         *aface = face;
152     }
153 
154     return error;
155   }
156 
157 
158   FT_CALLBACK_DEF( FT_Error )
ftc_basic_family_load_glyph(FTC_Family ftcfamily,FT_UInt gindex,FTC_Cache cache,FT_Glyph * aglyph)159   ftc_basic_family_load_glyph( FTC_Family  ftcfamily,
160                                FT_UInt     gindex,
161                                FTC_Cache   cache,
162                                FT_Glyph   *aglyph )
163   {
164     FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
165     FT_Error         error;
166     FTC_Scaler       scaler = &family->attrs.scaler;
167     FT_Face          face;
168     FT_Size          size;
169 
170 
171     /* we will now load the glyph image */
172     error = FTC_Manager_LookupSize( cache->manager,
173                                     scaler,
174                                     &size );
175     if ( !error )
176     {
177       face = size->face;
178 
179       error = FT_Load_Glyph( face,
180                              gindex,
181                              (FT_Int)family->attrs.load_flags );
182       if ( !error )
183       {
184         if ( face->glyph->format == FT_GLYPH_FORMAT_BITMAP  ||
185              face->glyph->format == FT_GLYPH_FORMAT_OUTLINE )
186         {
187           /* ok, copy it */
188           FT_Glyph  glyph;
189 
190 
191           error = FT_Get_Glyph( face->glyph, &glyph );
192           if ( !error )
193           {
194             *aglyph = glyph;
195             goto Exit;
196           }
197         }
198         else
199           error = FT_THROW( Invalid_Argument );
200       }
201     }
202 
203   Exit:
204     return error;
205   }
206 
207 
208   FT_CALLBACK_DEF( FT_Bool )
ftc_basic_gnode_compare_faceid(FTC_Node ftcgnode,FT_Pointer ftcface_id,FTC_Cache cache,FT_Bool * list_changed)209   ftc_basic_gnode_compare_faceid( FTC_Node    ftcgnode,
210                                   FT_Pointer  ftcface_id,
211                                   FTC_Cache   cache,
212                                   FT_Bool*    list_changed )
213   {
214     FTC_GNode        gnode   = (FTC_GNode)ftcgnode;
215     FTC_FaceID       face_id = (FTC_FaceID)ftcface_id;
216     FTC_BasicFamily  family  = (FTC_BasicFamily)gnode->family;
217     FT_Bool          result;
218 
219 
220     if ( list_changed )
221       *list_changed = FALSE;
222     result = FT_BOOL( family->attrs.scaler.face_id == face_id );
223     if ( result )
224     {
225       /* we must call this function to avoid this node from appearing
226        * in later lookups with the same face_id!
227        */
228       FTC_GNode_UnselectFamily( gnode, cache );
229     }
230     return result;
231   }
232 
233 
234  /*
235   *
236   * basic image cache
237   *
238   */
239 
240   static
241   const FTC_IFamilyClassRec  ftc_basic_image_family_class =
242   {
243     {
244       sizeof ( FTC_BasicFamilyRec ),
245 
246       ftc_basic_family_compare, /* FTC_MruNode_CompareFunc  node_compare */
247       ftc_basic_family_init,    /* FTC_MruNode_InitFunc     node_init    */
248       NULL,                     /* FTC_MruNode_ResetFunc    node_reset   */
249       NULL                      /* FTC_MruNode_DoneFunc     node_done    */
250     },
251 
252     ftc_basic_family_load_glyph /* FTC_IFamily_LoadGlyphFunc  family_load_glyph */
253   };
254 
255 
256   static
257   const FTC_GCacheClassRec  ftc_basic_image_cache_class =
258   {
259     {
260       ftc_inode_new,                  /* FTC_Node_NewFunc      node_new           */
261       ftc_inode_weight,               /* FTC_Node_WeightFunc   node_weight        */
262       ftc_gnode_compare,              /* FTC_Node_CompareFunc  node_compare       */
263       ftc_basic_gnode_compare_faceid, /* FTC_Node_CompareFunc  node_remove_faceid */
264       ftc_inode_free,                 /* FTC_Node_FreeFunc     node_free          */
265 
266       sizeof ( FTC_GCacheRec ),
267       ftc_gcache_init,                /* FTC_Cache_InitFunc    cache_init         */
268       ftc_gcache_done                 /* FTC_Cache_DoneFunc    cache_done         */
269     },
270 
271     (FTC_MruListClass)&ftc_basic_image_family_class
272   };
273 
274 
275   /* documentation is in ftcache.h */
276 
277   FT_EXPORT_DEF( FT_Error )
FTC_ImageCache_New(FTC_Manager manager,FTC_ImageCache * acache)278   FTC_ImageCache_New( FTC_Manager      manager,
279                       FTC_ImageCache  *acache )
280   {
281     return FTC_GCache_New( manager, &ftc_basic_image_cache_class,
282                            (FTC_GCache*)acache );
283   }
284 
285 
286   /* documentation is in ftcache.h */
287 
288   FT_EXPORT_DEF( FT_Error )
FTC_ImageCache_Lookup(FTC_ImageCache cache,FTC_ImageType type,FT_UInt gindex,FT_Glyph * aglyph,FTC_Node * anode)289   FTC_ImageCache_Lookup( FTC_ImageCache  cache,
290                          FTC_ImageType   type,
291                          FT_UInt         gindex,
292                          FT_Glyph       *aglyph,
293                          FTC_Node       *anode )
294   {
295     FTC_BasicQueryRec  query;
296     FTC_Node           node = 0; /* make compiler happy */
297     FT_Error           error;
298     FT_Offset          hash;
299 
300 
301     /* some argument checks are delayed to `FTC_Cache_Lookup' */
302     if ( !aglyph )
303     {
304       error = FT_THROW( Invalid_Argument );
305       goto Exit;
306     }
307 
308     *aglyph = NULL;
309     if ( anode )
310       *anode  = NULL;
311 
312     /*
313      * Internal `FTC_BasicAttr->load_flags' is of type `FT_UInt',
314      * but public `FT_ImageType->flags' is of type `FT_Int32'.
315      *
316      * On 16bit systems, higher bits of type->flags cannot be handled.
317      */
318 #if 0xFFFFFFFFUL > FT_UINT_MAX
319     if ( (type->flags & (FT_ULong)FT_UINT_MAX) )
320       FT_TRACE1(( "FTC_ImageCache_Lookup:"
321                   " higher bits in load_flags 0x%x are dropped\n",
322                   (FT_ULong)type->flags & ~((FT_ULong)FT_UINT_MAX) ));
323 #endif
324 
325     query.attrs.scaler.face_id = type->face_id;
326     query.attrs.scaler.width   = type->width;
327     query.attrs.scaler.height  = type->height;
328     query.attrs.load_flags     = (FT_UInt)type->flags;
329 
330     query.attrs.scaler.pixel = 1;
331     query.attrs.scaler.x_res = 0;  /* make compilers happy */
332     query.attrs.scaler.y_res = 0;
333 
334     hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
335 
336 #if 1  /* inlining is about 50% faster! */
337     FTC_GCACHE_LOOKUP_CMP( cache,
338                            ftc_basic_family_compare,
339                            FTC_GNode_Compare,
340                            hash, gindex,
341                            &query,
342                            node,
343                            error );
344 #else
345     error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
346                                hash, gindex,
347                                FTC_GQUERY( &query ),
348                                &node );
349 #endif
350     if ( !error )
351     {
352       *aglyph = FTC_INODE( node )->glyph;
353 
354       if ( anode )
355       {
356         *anode = node;
357         node->ref_count++;
358       }
359     }
360 
361   Exit:
362     return error;
363   }
364 
365 
366   /* documentation is in ftcache.h */
367 
368   FT_EXPORT_DEF( FT_Error )
FTC_ImageCache_LookupScaler(FTC_ImageCache cache,FTC_Scaler scaler,FT_ULong load_flags,FT_UInt gindex,FT_Glyph * aglyph,FTC_Node * anode)369   FTC_ImageCache_LookupScaler( FTC_ImageCache  cache,
370                                FTC_Scaler      scaler,
371                                FT_ULong        load_flags,
372                                FT_UInt         gindex,
373                                FT_Glyph       *aglyph,
374                                FTC_Node       *anode )
375   {
376     FTC_BasicQueryRec  query;
377     FTC_Node           node = 0; /* make compiler happy */
378     FT_Error           error;
379     FT_Offset          hash;
380 
381 
382     /* some argument checks are delayed to `FTC_Cache_Lookup' */
383     if ( !aglyph || !scaler )
384     {
385       error = FT_THROW( Invalid_Argument );
386       goto Exit;
387     }
388 
389     *aglyph = NULL;
390     if ( anode )
391       *anode  = NULL;
392 
393     /*
394      * Internal `FTC_BasicAttr->load_flags' is of type `FT_UInt',
395      * but public `FT_Face->face_flags' is of type `FT_Long'.
396      *
397      * On long > int systems, higher bits of load_flags cannot be handled.
398      */
399 #if FT_ULONG_MAX > FT_UINT_MAX
400     if ( load_flags > FT_UINT_MAX )
401       FT_TRACE1(( "FTC_ImageCache_LookupScaler:"
402                   " higher bits in load_flags 0x%lx are dropped\n",
403                   load_flags & ~((FT_ULong)FT_UINT_MAX) ));
404 #endif
405 
406     query.attrs.scaler     = scaler[0];
407     query.attrs.load_flags = (FT_UInt)load_flags;
408 
409     hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
410 
411     FTC_GCACHE_LOOKUP_CMP( cache,
412                            ftc_basic_family_compare,
413                            FTC_GNode_Compare,
414                            hash, gindex,
415                            &query,
416                            node,
417                            error );
418     if ( !error )
419     {
420       *aglyph = FTC_INODE( node )->glyph;
421 
422       if ( anode )
423       {
424         *anode = node;
425         node->ref_count++;
426       }
427     }
428 
429   Exit:
430     return error;
431   }
432 
433 
434   /*
435    *
436    * basic small bitmap cache
437    *
438    */
439 
440   static
441   const FTC_SFamilyClassRec  ftc_basic_sbit_family_class =
442   {
443     {
444       sizeof ( FTC_BasicFamilyRec ),
445       ftc_basic_family_compare,     /* FTC_MruNode_CompareFunc  node_compare */
446       ftc_basic_family_init,        /* FTC_MruNode_InitFunc     node_init    */
447       NULL,                         /* FTC_MruNode_ResetFunc    node_reset   */
448       NULL                          /* FTC_MruNode_DoneFunc     node_done    */
449     },
450 
451     ftc_basic_family_get_count,
452     ftc_basic_family_load_bitmap
453   };
454 
455 
456   static
457   const FTC_GCacheClassRec  ftc_basic_sbit_cache_class =
458   {
459     {
460       ftc_snode_new,                  /* FTC_Node_NewFunc      node_new           */
461       ftc_snode_weight,               /* FTC_Node_WeightFunc   node_weight        */
462       ftc_snode_compare,              /* FTC_Node_CompareFunc  node_compare       */
463       ftc_basic_gnode_compare_faceid, /* FTC_Node_CompareFunc  node_remove_faceid */
464       ftc_snode_free,                 /* FTC_Node_FreeFunc     node_free          */
465 
466       sizeof ( FTC_GCacheRec ),
467       ftc_gcache_init,                /* FTC_Cache_InitFunc    cache_init         */
468       ftc_gcache_done                 /* FTC_Cache_DoneFunc    cache_done         */
469     },
470 
471     (FTC_MruListClass)&ftc_basic_sbit_family_class
472   };
473 
474 
475   /* documentation is in ftcache.h */
476 
477   FT_EXPORT_DEF( FT_Error )
FTC_SBitCache_New(FTC_Manager manager,FTC_SBitCache * acache)478   FTC_SBitCache_New( FTC_Manager     manager,
479                      FTC_SBitCache  *acache )
480   {
481     return FTC_GCache_New( manager, &ftc_basic_sbit_cache_class,
482                            (FTC_GCache*)acache );
483   }
484 
485 
486   /* documentation is in ftcache.h */
487 
488   FT_EXPORT_DEF( FT_Error )
FTC_SBitCache_Lookup(FTC_SBitCache cache,FTC_ImageType type,FT_UInt gindex,FTC_SBit * ansbit,FTC_Node * anode)489   FTC_SBitCache_Lookup( FTC_SBitCache  cache,
490                         FTC_ImageType  type,
491                         FT_UInt        gindex,
492                         FTC_SBit      *ansbit,
493                         FTC_Node      *anode )
494   {
495     FT_Error           error;
496     FTC_BasicQueryRec  query;
497     FTC_Node           node = 0; /* make compiler happy */
498     FT_Offset          hash;
499 
500 
501     if ( anode )
502       *anode = NULL;
503 
504     /* other argument checks delayed to `FTC_Cache_Lookup' */
505     if ( !ansbit )
506       return FT_THROW( Invalid_Argument );
507 
508     *ansbit = NULL;
509 
510     /*
511      * Internal `FTC_BasicAttr->load_flags' is of type `FT_UInt',
512      * but public `FT_ImageType->flags' is of type `FT_Int32'.
513      *
514      * On 16bit systems, higher bits of type->flags cannot be handled.
515      */
516 #if 0xFFFFFFFFUL > FT_UINT_MAX
517     if ( (type->flags & (FT_ULong)FT_UINT_MAX) )
518       FT_TRACE1(( "FTC_ImageCache_Lookup:"
519                   " higher bits in load_flags 0x%x are dropped\n",
520                   (FT_ULong)type->flags & ~((FT_ULong)FT_UINT_MAX) ));
521 #endif
522 
523     query.attrs.scaler.face_id = type->face_id;
524     query.attrs.scaler.width   = type->width;
525     query.attrs.scaler.height  = type->height;
526     query.attrs.load_flags     = (FT_UInt)type->flags;
527 
528     query.attrs.scaler.pixel = 1;
529     query.attrs.scaler.x_res = 0;  /* make compilers happy */
530     query.attrs.scaler.y_res = 0;
531 
532     /* beware, the hash must be the same for all glyph ranges! */
533     hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
534            gindex / FTC_SBIT_ITEMS_PER_NODE;
535 
536 #if 1  /* inlining is about 50% faster! */
537     FTC_GCACHE_LOOKUP_CMP( cache,
538                            ftc_basic_family_compare,
539                            FTC_SNode_Compare,
540                            hash, gindex,
541                            &query,
542                            node,
543                            error );
544 #else
545     error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
546                                hash,
547                                gindex,
548                                FTC_GQUERY( &query ),
549                                &node );
550 #endif
551     if ( error )
552       goto Exit;
553 
554     *ansbit = FTC_SNODE( node )->sbits +
555               ( gindex - FTC_GNODE( node )->gindex );
556 
557     if ( anode )
558     {
559       *anode = node;
560       node->ref_count++;
561     }
562 
563   Exit:
564     return error;
565   }
566 
567 
568   /* documentation is in ftcache.h */
569 
570   FT_EXPORT_DEF( FT_Error )
FTC_SBitCache_LookupScaler(FTC_SBitCache cache,FTC_Scaler scaler,FT_ULong load_flags,FT_UInt gindex,FTC_SBit * ansbit,FTC_Node * anode)571   FTC_SBitCache_LookupScaler( FTC_SBitCache  cache,
572                               FTC_Scaler     scaler,
573                               FT_ULong       load_flags,
574                               FT_UInt        gindex,
575                               FTC_SBit      *ansbit,
576                               FTC_Node      *anode )
577   {
578     FT_Error           error;
579     FTC_BasicQueryRec  query;
580     FTC_Node           node = 0; /* make compiler happy */
581     FT_Offset          hash;
582 
583 
584     if ( anode )
585         *anode = NULL;
586 
587     /* other argument checks delayed to `FTC_Cache_Lookup' */
588     if ( !ansbit || !scaler )
589         return FT_THROW( Invalid_Argument );
590 
591     *ansbit = NULL;
592 
593     /*
594      * Internal `FTC_BasicAttr->load_flags' is of type `FT_UInt',
595      * but public `FT_Face->face_flags' is of type `FT_Long'.
596      *
597      * On long > int systems, higher bits of load_flags cannot be handled.
598      */
599 #if FT_ULONG_MAX > FT_UINT_MAX
600     if ( load_flags > FT_UINT_MAX )
601       FT_TRACE1(( "FTC_ImageCache_LookupScaler:"
602                   " higher bits in load_flags 0x%lx are dropped\n",
603                   load_flags & ~((FT_ULong)FT_UINT_MAX) ));
604 #endif
605 
606     query.attrs.scaler     = scaler[0];
607     query.attrs.load_flags = (FT_UInt)load_flags;
608 
609     /* beware, the hash must be the same for all glyph ranges! */
610     hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
611              gindex / FTC_SBIT_ITEMS_PER_NODE;
612 
613     FTC_GCACHE_LOOKUP_CMP( cache,
614                            ftc_basic_family_compare,
615                            FTC_SNode_Compare,
616                            hash, gindex,
617                            &query,
618                            node,
619                            error );
620     if ( error )
621       goto Exit;
622 
623     *ansbit = FTC_SNODE( node )->sbits +
624               ( gindex - FTC_GNODE( node )->gindex );
625 
626     if ( anode )
627     {
628       *anode = node;
629       node->ref_count++;
630     }
631 
632   Exit:
633     return error;
634   }
635 
636 
637 /* END */
638