]> granicus.if.org Git - libass/blob - libass/ass_cache.c
Initial libass release (without mencoder support).
[libass] / libass / ass_cache.c
1 #include "config.h"
2
3 #include <ft2build.h>
4 #include FT_FREETYPE_H
5
6 #include <assert.h>
7
8 #include "mp_msg.h"
9 #include "ass_fontconfig.h"
10 #include "ass_cache.h"
11
12
13 typedef struct face_cache_item_s {
14         face_desc_t desc;
15         char* path;
16         int index;
17         FT_Face face;
18 } face_cache_item_t;
19
20 #define MAX_FACE_CACHE_SIZE 100
21
22 static face_cache_item_t* face_cache;
23 static int face_cache_size;
24
25 extern int no_more_font_messages;
26
27 static int font_compare(face_desc_t* a, face_desc_t* b) {
28         if (strcmp(a->family, b->family) != 0)
29                 return 0;
30         if (a->bold != b->bold)
31                 return 0;
32         if (a->italic != b->italic)
33                 return 0;
34         return 1;
35 }
36
37 /**
38  * \brief Get a face object, either from cache or created through FreeType+FontConfig.
39  * \param library FreeType library object
40  * \param fontconfig_priv fontconfig private data
41  * \param desc required face description
42  * \param face out: the face object
43 */ 
44 int ass_new_face(FT_Library library, void* fontconfig_priv, face_desc_t* desc, /*out*/ FT_Face* face)
45 {
46         FT_Error error;
47         int i;
48         char* path;
49         int index;
50         face_cache_item_t* item;
51         
52         for (i=0; i<face_cache_size; ++i)
53                 if (font_compare(desc, &(face_cache[i].desc))) {
54                         *face = face_cache[i].face;
55                         return 0;
56                 }
57
58         if (face_cache_size == MAX_FACE_CACHE_SIZE) {
59                 mp_msg(MSGT_GLOBAL, MSGL_FATAL, "Too many fonts\n");
60                 return 1;
61         }
62
63         path = fontconfig_select(fontconfig_priv, desc->family, desc->bold, desc->italic, &index);
64         
65         error = FT_New_Face(library, path, index, face);
66         if (error) {
67                 if (!no_more_font_messages)
68                         mp_msg(MSGT_GLOBAL, MSGL_WARN, "Error opening font: %s, %d\n", path, index);
69                 no_more_font_messages = 1;
70                 return 1;
71         }
72         
73         item = face_cache + face_cache_size;
74         item->path = strdup(path);
75         item->index = index;
76         item->face = *face;
77         memcpy(&(item->desc), desc, sizeof(face_desc_t));
78         face_cache_size++;
79         return 0;
80 }
81
82 void ass_face_cache_init(void)
83 {
84         face_cache = calloc(MAX_FACE_CACHE_SIZE, sizeof(face_cache_item_t));
85         face_cache_size = 0;
86 }
87
88 void ass_face_cache_done(void)
89 {
90         int i;
91         for (i = 0; i < face_cache_size; ++i) {
92                 face_cache_item_t* item = face_cache + i;
93                 if (item->face) FT_Done_Face(item->face);
94                 if (item->path) free(item->path);
95                 // FIXME: free desc ?
96         }
97         free(face_cache);
98         face_cache_size = 0;
99 }
100
101 //---------------------------------
102 // glyph cache
103
104 #define GLYPH_HASH_SIZE (0xFFFF + 13)
105
106 typedef struct glyph_hash_item_s {
107         glyph_hash_key_t key;
108         glyph_hash_val_t val;
109         struct glyph_hash_item_s* next;
110 } glyph_hash_item_t;
111
112 typedef glyph_hash_item_t* glyph_hash_item_p;
113
114 static glyph_hash_item_p* glyph_hash_root;
115 static int glyph_hash_size;
116
117 static int glyph_compare(glyph_hash_key_t* a, glyph_hash_key_t* b) {
118         if (memcmp(a, b, sizeof(glyph_hash_key_t)) == 0)
119                 return 1;
120         else
121                 return 0;
122 }
123
124 static unsigned glyph_hash(glyph_hash_key_t* key) {
125         unsigned val = 0;
126         unsigned i;
127         for (i = 0; i < sizeof(key->face); ++i)
128                 val += *(unsigned char *)(&(key->face) + i);
129         val <<= 21;
130         
131         if (key->bitmap)   val &= 0x80000000;
132         val += key->index;
133         val += key->size << 8;
134         val += key->outline << 3;
135         val += key->advance.x << 10;
136         val += key->advance.y << 16;
137         val += key->bold << 1;
138         val += key->italic << 20;
139         return val;
140 }
141
142 /**
143  * \brief Add a glyph to glyph cache.
144  * \param key hash key
145  * \param val hash val: 2 bitmap glyphs + some additional info
146 */ 
147 void cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val)
148 {
149         unsigned hash = glyph_hash(key);
150         glyph_hash_item_t** next = glyph_hash_root + (hash % GLYPH_HASH_SIZE);
151         while (*next) {
152                 if (glyph_compare(key, &((*next)->key)))
153                         return;
154                 next = &((*next)->next);
155                 assert(next);
156         }
157         (*next) = malloc(sizeof(glyph_hash_item_t));
158 //      (*next)->desc = glyph_key_copy(key, &((*next)->key));
159         memcpy(&((*next)->key), key, sizeof(glyph_hash_key_t));
160         memcpy(&((*next)->val), val, sizeof(glyph_hash_val_t));
161         (*next)->next = 0;
162
163         glyph_hash_size ++;
164 /*      if (glyph_hash_size  && (glyph_hash_size % 25 == 0)) {
165                 printf("\nGlyph cache: %d entries, %d bytes\n", glyph_hash_size, glyph_hash_size * sizeof(glyph_hash_item_t));
166         } */
167 }
168
169 /**
170  * \brief Get a glyph from glyph cache.
171  * \param key hash key
172  * \return requested hash val or 0 if not found
173 */ 
174 glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key)
175 {
176         unsigned hash = glyph_hash(key);
177         glyph_hash_item_t* item = glyph_hash_root[hash % GLYPH_HASH_SIZE];
178         while (item) {
179                 if (glyph_compare(key, &(item->key))) {
180                         return &(item->val);
181                 }
182                 item = item->next;
183         }
184         return 0;
185 }
186
187 void ass_glyph_cache_init(void)
188 {
189         glyph_hash_root = calloc(GLYPH_HASH_SIZE, sizeof(glyph_hash_item_p));
190         glyph_hash_size = 0;
191 }
192
193 void ass_glyph_cache_done(void)
194 {
195         int i;
196         for (i = 0; i < GLYPH_HASH_SIZE; ++i) {
197                 glyph_hash_item_t* item = glyph_hash_root[i];
198                 while (item) {
199                         glyph_hash_item_t* next = item->next;
200                         if (item->val.glyph) FT_Done_Glyph(item->val.glyph);
201                         if (item->val.outline_glyph) FT_Done_Glyph(item->val.outline_glyph);
202                         free(item);
203                         item = next;
204                 }
205         }
206         free(glyph_hash_root);
207         glyph_hash_size = 0;
208 }
209