]> granicus.if.org Git - libass/blob - libass/ass_cache.c
Convert outline processing and caching from glyphs to bare outlines
[libass] / libass / ass_cache.c
1 /*
2  * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3  * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
4  *
5  * This file is part of libass.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include "config.h"
21
22 #include <inttypes.h>
23 #include <ft2build.h>
24 #include FT_FREETYPE_H
25 #include FT_OUTLINE_H
26
27 #include <assert.h>
28
29 #include "ass_utils.h"
30 #include "ass.h"
31 #include "ass_fontconfig.h"
32 #include "ass_font.h"
33 #include "ass_bitmap.h"
34 #include "ass_cache.h"
35
36 // type-specific functions
37 // create hash/compare functions for bitmap, glyph and composite cache
38 #define CREATE_HASH_FUNCTIONS
39 #include "ass_cache_template.h"
40 #define CREATE_COMPARISON_FUNCTIONS
41 #include "ass_cache_template.h"
42
43 // font cache
44 static unsigned font_hash(void *buf, size_t len)
45 {
46     ASS_FontDesc *desc = buf;
47     unsigned hval;
48     hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
49     hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
50     hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
51     hval = fnv_32a_buf(&desc->treat_family_as_pattern,
52             sizeof(desc->treat_family_as_pattern), hval);
53     hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval);
54     return hval;
55 }
56
57 static unsigned font_compare(void *key1, void *key2, size_t key_size)
58 {
59     ASS_FontDesc *a = key1;
60     ASS_FontDesc *b = key2;
61     if (strcmp(a->family, b->family) != 0)
62         return 0;
63     if (a->bold != b->bold)
64         return 0;
65     if (a->italic != b->italic)
66         return 0;
67     if (a->treat_family_as_pattern != b->treat_family_as_pattern)
68         return 0;
69     if (a->vertical != b->vertical)
70         return 0;
71     return 1;
72 }
73
74 static void font_destruct(void *key, void *value)
75 {
76     ass_font_free(value);
77     free(key);
78 }
79
80 // bitmap cache
81 static void bitmap_destruct(void *key, void *value)
82 {
83     BitmapHashValue *v = value;
84     if (v->bm)
85         ass_free_bitmap(v->bm);
86     if (v->bm_o)
87         ass_free_bitmap(v->bm_o);
88     if (v->bm_s)
89         ass_free_bitmap(v->bm_s);
90     free(key);
91     free(value);
92 }
93
94 static size_t bitmap_size(void *value, size_t value_size)
95 {
96     BitmapHashValue *val = value;
97     if (val->bm_o)
98         return val->bm_o->w * val->bm_o->h * 3;
99     else if (val->bm)
100         return val->bm->w * val->bm->h * 3;
101     return 0;
102 }
103
104 // glyph cache
105 static void glyph_destruct(void *key, void *value)
106 {
107     GlyphHashValue *v = value;
108     if (v->outline)
109         outline_free(v->lib, v->outline);
110     if (v->border)
111         outline_free(v->lib, v->border);
112     free(key);
113     free(value);
114 }
115
116 static size_t glyph_size(void *value, size_t value_size)
117 {
118 #if 0
119     GlyphHashValue *val = value;
120     if (val->glyph && val->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
121         FT_Bitmap *bitmap = &((FT_BitmapGlyph) val->glyph)->bitmap;
122         return bitmap->rows * bitmap->pitch;
123     }
124 #endif
125     return 0;
126 }
127
128 // composite cache
129 static void composite_destruct(void *key, void *value)
130 {
131     CompositeHashValue *v = value;
132     free(v->a);
133     free(v->b);
134     free(key);
135     free(value);
136 }
137
138
139 // Cache data
140 typedef struct cache_item {
141     void *key;
142     void *value;
143     struct cache_item *next;
144 } CacheItem;
145
146 struct cache {
147     unsigned buckets;
148     CacheItem **map;
149
150     HashFunction hash_func;
151     ItemSize size_func;
152     HashCompare compare_func;
153     CacheItemDestructor destruct_func;
154     size_t key_size;
155     size_t value_size;
156
157     size_t cache_size;
158     unsigned hits;
159     unsigned misses;
160     unsigned items;
161 };
162
163 // Hash for a simple (single value or array) type
164 static unsigned hash_simple(void *key, size_t key_size)
165 {
166     return fnv_32a_buf(key, key_size, FNV1_32A_INIT);
167 }
168
169 // Comparison of a simple type
170 static unsigned compare_simple(void *a, void *b, size_t key_size)
171 {
172     return memcmp(a, b, key_size) == 0;
173 }
174
175 // Default destructor
176 static void destruct_simple(void *key, void *value)
177 {
178     free(key);
179     free(value);
180 }
181
182
183 // Create a cache with type-specific hash/compare/destruct/size functions
184 Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func,
185                         CacheItemDestructor destruct_func, ItemSize size_func,
186                         size_t key_size, size_t value_size)
187 {
188     Cache *cache = calloc(1, sizeof(*cache));
189     cache->buckets = 0xFFFF;
190     cache->hash_func = hash_simple;
191     cache->compare_func = compare_simple;
192     cache->destruct_func = destruct_simple;
193     cache->size_func = size_func;
194     if (hash_func)
195         cache->hash_func = hash_func;
196     if (compare_func)
197         cache->compare_func = compare_func;
198     if (destruct_func)
199         cache->destruct_func = destruct_func;
200     cache->key_size = key_size;
201     cache->value_size = value_size;
202     cache->map = calloc(cache->buckets, sizeof(CacheItem *));
203
204     return cache;
205 }
206
207 void *ass_cache_put(Cache *cache, void *key, void *value)
208 {
209     unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets;
210     CacheItem **item = &cache->map[bucket];
211     while (*item)
212         *item = (*item)->next;
213     (*item) = calloc(1, sizeof(CacheItem));
214     (*item)->key = malloc(cache->key_size);
215     (*item)->value = malloc(cache->value_size);
216     memcpy((*item)->key, key, cache->key_size);
217     memcpy((*item)->value, value, cache->value_size);
218
219     cache->items++;
220     if (cache->size_func)
221         cache->cache_size += cache->size_func(value, cache->value_size);
222
223     return (*item)->value;
224 }
225
226 void *ass_cache_get(Cache *cache, void *key)
227 {
228     unsigned bucket = cache->hash_func(key, cache->key_size) % cache->buckets;
229     CacheItem *item = cache->map[bucket];
230     while (item) {
231         if (cache->compare_func(key, item->key, cache->key_size)) {
232             cache->hits++;
233             return item->value;
234         }
235         item = item->next;
236     }
237     cache->misses++;
238     return NULL;
239 }
240
241 size_t ass_cache_empty(Cache *cache, size_t max_size)
242 {
243     int i;
244
245     if (cache->cache_size < max_size)
246         return cache->cache_size;
247
248     for (i = 0; i < cache->buckets; i++) {
249         CacheItem *item = cache->map[i];
250         while (item) {
251             CacheItem *next = item->next;
252             cache->destruct_func(item->key, item->value);
253             free(item);
254             item = next;
255         }
256         cache->map[i] = NULL;
257     }
258
259     cache->items = cache->hits = cache->misses = cache->cache_size = 0;
260
261     return 0;
262 }
263
264 void ass_cache_stats(Cache *cache, size_t *size, unsigned *hits,
265                      unsigned *misses, unsigned *count)
266 {
267     *size = cache->cache_size;
268     *hits = cache->hits;
269     *misses = cache->misses;
270     *count = cache->items;
271 }
272
273 void ass_cache_done(Cache *cache)
274 {
275     ass_cache_empty(cache, 0);
276     free(cache->map);
277     free(cache);
278 }
279
280 // Type-specific creation function
281 Cache *ass_font_cache_create(void)
282 {
283     return ass_cache_create(font_hash, font_compare, font_destruct,
284             (ItemSize)NULL, sizeof(ASS_FontDesc), sizeof(ASS_Font));
285 }
286
287 Cache *ass_glyph_cache_create(void)
288 {
289     return ass_cache_create(glyph_hash, glyph_compare, glyph_destruct,
290             glyph_size, sizeof(GlyphHashKey), sizeof(GlyphHashValue));
291 }
292
293 Cache *ass_bitmap_cache_create(void)
294 {
295     return ass_cache_create(bitmap_hash, bitmap_compare, bitmap_destruct,
296             bitmap_size, sizeof(BitmapHashKey), sizeof(BitmapHashValue));
297 }
298
299 Cache *ass_composite_cache_create(void)
300 {
301     return ass_cache_create(composite_hash, composite_compare,
302             composite_destruct, (ItemSize)NULL, sizeof(CompositeHashKey),
303             sizeof(CompositeHashValue));
304 }