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