1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* ftcsbits.c */ |
|
4 /* */ |
|
5 /* FreeType sbits manager (body). */ |
|
6 /* */ |
|
7 /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, 2011 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_CACHE_H |
|
21 #include "ftcsbits.h" |
|
22 #include FT_INTERNAL_OBJECTS_H |
|
23 #include FT_INTERNAL_DEBUG_H |
|
24 #include FT_ERRORS_H |
|
25 |
|
26 #include "ftccback.h" |
|
27 #include "ftcerror.h" |
|
28 |
|
29 #undef FT_COMPONENT |
|
30 #define FT_COMPONENT trace_cache |
|
31 |
|
32 |
|
33 /*************************************************************************/ |
|
34 /*************************************************************************/ |
|
35 /***** *****/ |
|
36 /***** SBIT CACHE NODES *****/ |
|
37 /***** *****/ |
|
38 /*************************************************************************/ |
|
39 /*************************************************************************/ |
|
40 |
|
41 |
|
42 static FT_Error |
|
43 ftc_sbit_copy_bitmap( FTC_SBit sbit, |
|
44 FT_Bitmap* bitmap, |
|
45 FT_Memory memory ) |
|
46 { |
|
47 FT_Error error; |
|
48 FT_Int pitch = bitmap->pitch; |
|
49 FT_ULong size; |
|
50 |
|
51 |
|
52 if ( pitch < 0 ) |
|
53 pitch = -pitch; |
|
54 |
|
55 size = (FT_ULong)( pitch * bitmap->rows ); |
|
56 |
|
57 if ( !FT_ALLOC( sbit->buffer, size ) ) |
|
58 FT_MEM_COPY( sbit->buffer, bitmap->buffer, size ); |
|
59 |
|
60 return error; |
|
61 } |
|
62 |
|
63 |
|
64 FT_LOCAL_DEF( void ) |
|
65 ftc_snode_free( FTC_Node ftcsnode, |
|
66 FTC_Cache cache ) |
|
67 { |
|
68 FTC_SNode snode = (FTC_SNode)ftcsnode; |
|
69 FTC_SBit sbit = snode->sbits; |
|
70 FT_UInt count = snode->count; |
|
71 FT_Memory memory = cache->memory; |
|
72 |
|
73 |
|
74 for ( ; count > 0; sbit++, count-- ) |
|
75 FT_FREE( sbit->buffer ); |
|
76 |
|
77 FTC_GNode_Done( FTC_GNODE( snode ), cache ); |
|
78 |
|
79 FT_FREE( snode ); |
|
80 } |
|
81 |
|
82 |
|
83 FT_LOCAL_DEF( void ) |
|
84 FTC_SNode_Free( FTC_SNode snode, |
|
85 FTC_Cache cache ) |
|
86 { |
|
87 ftc_snode_free( FTC_NODE( snode ), cache ); |
|
88 } |
|
89 |
|
90 |
|
91 /* |
|
92 * This function tries to load a small bitmap within a given FTC_SNode. |
|
93 * Note that it returns a non-zero error code _only_ in the case of |
|
94 * out-of-memory condition. For all other errors (e.g., corresponding |
|
95 * to a bad font file), this function will mark the sbit as `unavailable' |
|
96 * and return a value of 0. |
|
97 * |
|
98 * You should also read the comment within the @ftc_snode_compare |
|
99 * function below to see how out-of-memory is handled during a lookup. |
|
100 */ |
|
101 static FT_Error |
|
102 ftc_snode_load( FTC_SNode snode, |
|
103 FTC_Manager manager, |
|
104 FT_UInt gindex, |
|
105 FT_ULong *asize ) |
|
106 { |
|
107 FT_Error error; |
|
108 FTC_GNode gnode = FTC_GNODE( snode ); |
|
109 FTC_Family family = gnode->family; |
|
110 FT_Memory memory = manager->memory; |
|
111 FT_Face face; |
|
112 FTC_SBit sbit; |
|
113 FTC_SFamilyClass clazz; |
|
114 |
|
115 |
|
116 if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count ) |
|
117 { |
|
118 FT_ERROR(( "ftc_snode_load: invalid glyph index" )); |
|
119 return FTC_Err_Invalid_Argument; |
|
120 } |
|
121 |
|
122 sbit = snode->sbits + ( gindex - gnode->gindex ); |
|
123 clazz = (FTC_SFamilyClass)family->clazz; |
|
124 |
|
125 sbit->buffer = 0; |
|
126 |
|
127 error = clazz->family_load_glyph( family, gindex, manager, &face ); |
|
128 if ( error ) |
|
129 goto BadGlyph; |
|
130 |
|
131 { |
|
132 FT_Int temp; |
|
133 FT_GlyphSlot slot = face->glyph; |
|
134 FT_Bitmap* bitmap = &slot->bitmap; |
|
135 FT_Pos xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */ |
|
136 |
|
137 |
|
138 if ( slot->format != FT_GLYPH_FORMAT_BITMAP ) |
|
139 { |
|
140 FT_TRACE0(( "ftc_snode_load:" |
|
141 " glyph loaded didn't return a bitmap\n" )); |
|
142 goto BadGlyph; |
|
143 } |
|
144 |
|
145 /* Check that our values fit into 8-bit containers! */ |
|
146 /* If this is not the case, our bitmap is too large */ |
|
147 /* and we will leave it as `missing' with sbit.buffer = 0 */ |
|
148 |
|
149 #define CHECK_CHAR( d ) ( temp = (FT_Char)d, temp == d ) |
|
150 #define CHECK_BYTE( d ) ( temp = (FT_Byte)d, temp == d ) |
|
151 |
|
152 /* horizontal advance in pixels */ |
|
153 xadvance = ( slot->advance.x + 32 ) >> 6; |
|
154 yadvance = ( slot->advance.y + 32 ) >> 6; |
|
155 |
|
156 if ( !CHECK_BYTE( bitmap->rows ) || |
|
157 !CHECK_BYTE( bitmap->width ) || |
|
158 !CHECK_CHAR( bitmap->pitch ) || |
|
159 !CHECK_CHAR( slot->bitmap_left ) || |
|
160 !CHECK_CHAR( slot->bitmap_top ) || |
|
161 !CHECK_CHAR( xadvance ) || |
|
162 !CHECK_CHAR( yadvance ) ) |
|
163 { |
|
164 FT_TRACE2(( "ftc_snode_load:" |
|
165 " glyph too large for small bitmap cache\n")); |
|
166 goto BadGlyph; |
|
167 } |
|
168 |
|
169 sbit->width = (FT_Byte)bitmap->width; |
|
170 sbit->height = (FT_Byte)bitmap->rows; |
|
171 sbit->pitch = (FT_Char)bitmap->pitch; |
|
172 sbit->left = (FT_Char)slot->bitmap_left; |
|
173 sbit->top = (FT_Char)slot->bitmap_top; |
|
174 sbit->xadvance = (FT_Char)xadvance; |
|
175 sbit->yadvance = (FT_Char)yadvance; |
|
176 sbit->format = (FT_Byte)bitmap->pixel_mode; |
|
177 sbit->max_grays = (FT_Byte)(bitmap->num_grays - 1); |
|
178 |
|
179 /* copy the bitmap into a new buffer -- ignore error */ |
|
180 error = ftc_sbit_copy_bitmap( sbit, bitmap, memory ); |
|
181 |
|
182 /* now, compute size */ |
|
183 if ( asize ) |
|
184 *asize = FT_ABS( sbit->pitch ) * sbit->height; |
|
185 |
|
186 } /* glyph loading successful */ |
|
187 |
|
188 /* ignore the errors that might have occurred -- */ |
|
189 /* we mark unloaded glyphs with `sbit.buffer == 0' */ |
|
190 /* and `width == 255', `height == 0' */ |
|
191 /* */ |
|
192 if ( error && error != FTC_Err_Out_Of_Memory ) |
|
193 { |
|
194 BadGlyph: |
|
195 sbit->width = 255; |
|
196 sbit->height = 0; |
|
197 sbit->buffer = NULL; |
|
198 error = FTC_Err_Ok; |
|
199 if ( asize ) |
|
200 *asize = 0; |
|
201 } |
|
202 |
|
203 return error; |
|
204 } |
|
205 |
|
206 |
|
207 FT_LOCAL_DEF( FT_Error ) |
|
208 FTC_SNode_New( FTC_SNode *psnode, |
|
209 FTC_GQuery gquery, |
|
210 FTC_Cache cache ) |
|
211 { |
|
212 FT_Memory memory = cache->memory; |
|
213 FT_Error error; |
|
214 FTC_SNode snode = NULL; |
|
215 FT_UInt gindex = gquery->gindex; |
|
216 FTC_Family family = gquery->family; |
|
217 |
|
218 FTC_SFamilyClass clazz = FTC_CACHE__SFAMILY_CLASS( cache ); |
|
219 FT_UInt total; |
|
220 FT_UInt node_count; |
|
221 |
|
222 |
|
223 total = clazz->family_get_count( family, cache->manager ); |
|
224 if ( total == 0 || gindex >= total ) |
|
225 { |
|
226 error = FTC_Err_Invalid_Argument; |
|
227 goto Exit; |
|
228 } |
|
229 |
|
230 if ( !FT_NEW( snode ) ) |
|
231 { |
|
232 FT_UInt count, start; |
|
233 |
|
234 |
|
235 start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE ); |
|
236 count = total - start; |
|
237 if ( count > FTC_SBIT_ITEMS_PER_NODE ) |
|
238 count = FTC_SBIT_ITEMS_PER_NODE; |
|
239 |
|
240 FTC_GNode_Init( FTC_GNODE( snode ), start, family ); |
|
241 |
|
242 snode->count = count; |
|
243 for ( node_count = 0; node_count < count; node_count++ ) |
|
244 { |
|
245 snode->sbits[node_count].width = 255; |
|
246 } |
|
247 |
|
248 error = ftc_snode_load( snode, |
|
249 cache->manager, |
|
250 gindex, |
|
251 NULL ); |
|
252 if ( error ) |
|
253 { |
|
254 FTC_SNode_Free( snode, cache ); |
|
255 snode = NULL; |
|
256 } |
|
257 } |
|
258 |
|
259 Exit: |
|
260 *psnode = snode; |
|
261 return error; |
|
262 } |
|
263 |
|
264 |
|
265 FT_LOCAL_DEF( FT_Error ) |
|
266 ftc_snode_new( FTC_Node *ftcpsnode, |
|
267 FT_Pointer ftcgquery, |
|
268 FTC_Cache cache ) |
|
269 { |
|
270 FTC_SNode *psnode = (FTC_SNode*)ftcpsnode; |
|
271 FTC_GQuery gquery = (FTC_GQuery)ftcgquery; |
|
272 |
|
273 |
|
274 return FTC_SNode_New( psnode, gquery, cache ); |
|
275 } |
|
276 |
|
277 |
|
278 FT_LOCAL_DEF( FT_Offset ) |
|
279 ftc_snode_weight( FTC_Node ftcsnode, |
|
280 FTC_Cache cache ) |
|
281 { |
|
282 FTC_SNode snode = (FTC_SNode)ftcsnode; |
|
283 FT_UInt count = snode->count; |
|
284 FTC_SBit sbit = snode->sbits; |
|
285 FT_Int pitch; |
|
286 FT_Offset size; |
|
287 |
|
288 FT_UNUSED( cache ); |
|
289 |
|
290 |
|
291 FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE ); |
|
292 |
|
293 /* the node itself */ |
|
294 size = sizeof ( *snode ); |
|
295 |
|
296 for ( ; count > 0; count--, sbit++ ) |
|
297 { |
|
298 if ( sbit->buffer ) |
|
299 { |
|
300 pitch = sbit->pitch; |
|
301 if ( pitch < 0 ) |
|
302 pitch = -pitch; |
|
303 |
|
304 /* add the size of a given glyph image */ |
|
305 size += pitch * sbit->height; |
|
306 } |
|
307 } |
|
308 |
|
309 return size; |
|
310 } |
|
311 |
|
312 |
|
313 #if 0 |
|
314 |
|
315 FT_LOCAL_DEF( FT_Offset ) |
|
316 FTC_SNode_Weight( FTC_SNode snode ) |
|
317 { |
|
318 return ftc_snode_weight( FTC_NODE( snode ), NULL ); |
|
319 } |
|
320 |
|
321 #endif /* 0 */ |
|
322 |
|
323 |
|
324 FT_LOCAL_DEF( FT_Bool ) |
|
325 ftc_snode_compare( FTC_Node ftcsnode, |
|
326 FT_Pointer ftcgquery, |
|
327 FTC_Cache cache, |
|
328 FT_Bool* list_changed ) |
|
329 { |
|
330 FTC_SNode snode = (FTC_SNode)ftcsnode; |
|
331 FTC_GQuery gquery = (FTC_GQuery)ftcgquery; |
|
332 FTC_GNode gnode = FTC_GNODE( snode ); |
|
333 FT_UInt gindex = gquery->gindex; |
|
334 FT_Bool result; |
|
335 |
|
336 |
|
337 if (list_changed) |
|
338 *list_changed = FALSE; |
|
339 result = FT_BOOL( gnode->family == gquery->family && |
|
340 (FT_UInt)( gindex - gnode->gindex ) < snode->count ); |
|
341 if ( result ) |
|
342 { |
|
343 /* check if we need to load the glyph bitmap now */ |
|
344 FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex ); |
|
345 |
|
346 |
|
347 /* |
|
348 * The following code illustrates what to do when you want to |
|
349 * perform operations that may fail within a lookup function. |
|
350 * |
|
351 * Here, we want to load a small bitmap on-demand; we thus |
|
352 * need to call the `ftc_snode_load' function which may return |
|
353 * a non-zero error code only when we are out of memory (OOM). |
|
354 * |
|
355 * The correct thing to do is to use @FTC_CACHE_TRYLOOP and |
|
356 * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop |
|
357 * that is capable of flushing the cache incrementally when |
|
358 * an OOM errors occur. |
|
359 * |
|
360 * However, we need to `lock' the node before this operation to |
|
361 * prevent it from being flushed within the loop. |
|
362 * |
|
363 * When we exit the loop, we unlock the node, then check the `error' |
|
364 * variable. If it is non-zero, this means that the cache was |
|
365 * completely flushed and that no usable memory was found to load |
|
366 * the bitmap. |
|
367 * |
|
368 * We then prefer to return a value of 0 (i.e., NO MATCH). This |
|
369 * ensures that the caller will try to allocate a new node. |
|
370 * This operation consequently _fail_ and the lookup function |
|
371 * returns the appropriate OOM error code. |
|
372 * |
|
373 * Note that `buffer == NULL && width == 255' is a hack used to |
|
374 * tag `unavailable' bitmaps in the array. We should never try |
|
375 * to load these. |
|
376 * |
|
377 */ |
|
378 |
|
379 if ( sbit->buffer == NULL && sbit->width == 255 ) |
|
380 { |
|
381 FT_ULong size; |
|
382 FT_Error error; |
|
383 |
|
384 |
|
385 ftcsnode->ref_count++; /* lock node to prevent flushing */ |
|
386 /* in retry loop */ |
|
387 |
|
388 FTC_CACHE_TRYLOOP( cache ) |
|
389 { |
|
390 error = ftc_snode_load( snode, cache->manager, gindex, &size ); |
|
391 } |
|
392 FTC_CACHE_TRYLOOP_END( list_changed ); |
|
393 |
|
394 ftcsnode->ref_count--; /* unlock the node */ |
|
395 |
|
396 if ( error ) |
|
397 result = 0; |
|
398 else |
|
399 cache->manager->cur_weight += size; |
|
400 } |
|
401 } |
|
402 |
|
403 return result; |
|
404 } |
|
405 |
|
406 |
|
407 #ifdef FTC_INLINE |
|
408 |
|
409 FT_LOCAL_DEF( FT_Bool ) |
|
410 FTC_SNode_Compare( FTC_SNode snode, |
|
411 FTC_GQuery gquery, |
|
412 FTC_Cache cache, |
|
413 FT_Bool* list_changed ) |
|
414 { |
|
415 return ftc_snode_compare( FTC_NODE( snode ), gquery, |
|
416 cache, list_changed ); |
|
417 } |
|
418 |
|
419 #endif |
|
420 |
|
421 /* END */ |
|