1 /****************************************************************************
5 * FreeType sbits manager (body).
7 * Copyright (C) 2000-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
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.
19 #include <freetype/ftcache.h>
21 #include <freetype/internal/ftobjs.h>
22 #include <freetype/internal/ftdebug.h>
23 #include <freetype/fterrors.h>
29 #define FT_COMPONENT cache
32 /*************************************************************************/
33 /*************************************************************************/
35 /***** SBIT CACHE NODES *****/
37 /*************************************************************************/
38 /*************************************************************************/
42 ftc_sbit_copy_bitmap( FTC_SBit sbit,
47 FT_Int pitch = bitmap->pitch;
54 size = (FT_ULong)pitch * bitmap->rows;
56 if ( !FT_QALLOC( sbit->buffer, size ) )
57 FT_MEM_COPY( sbit->buffer, bitmap->buffer, size );
64 ftc_snode_free( FTC_Node ftcsnode,
67 FTC_SNode snode = (FTC_SNode)ftcsnode;
68 FTC_SBit sbit = snode->sbits;
69 FT_UInt count = snode->count;
70 FT_Memory memory = cache->memory;
73 for ( ; count > 0; sbit++, count-- )
74 FT_FREE( sbit->buffer );
76 FTC_GNode_Done( FTC_GNODE( snode ), cache );
83 FTC_SNode_Free( FTC_SNode snode,
86 ftc_snode_free( FTC_NODE( snode ), cache );
91 * This function tries to load a small bitmap within a given FTC_SNode.
92 * Note that it returns a non-zero error code _only_ in the case of
93 * out-of-memory condition. For all other errors (e.g., corresponding
94 * to a bad font file), this function will mark the sbit as `unavailable'
95 * and return a value of 0.
97 * You should also read the comment within the @ftc_snode_compare
98 * function below to see how out-of-memory is handled during a lookup.
101 ftc_snode_load( FTC_SNode snode,
107 FTC_GNode gnode = FTC_GNODE( snode );
108 FTC_Family family = gnode->family;
111 FTC_SFamilyClass clazz;
114 if ( gindex - gnode->gindex >= snode->count )
116 FT_ERROR(( "ftc_snode_load: invalid glyph index" ));
117 return FT_THROW( Invalid_Argument );
120 sbit = snode->sbits + ( gindex - gnode->gindex );
121 clazz = (FTC_SFamilyClass)family->clazz;
123 error = clazz->family_load_glyph( family, gindex, manager, &face );
129 FT_GlyphSlot slot = face->glyph;
130 FT_Bitmap* bitmap = &slot->bitmap;
131 FT_Pos xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */
134 if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
136 FT_TRACE0(( "ftc_snode_load:"
137 " glyph loaded didn't return a bitmap\n" ));
141 /* Check whether our values fit into 8/16-bit containers! */
142 /* If this is not the case, our bitmap is too large */
143 /* and we will leave it as `missing' with sbit.buffer = 0 */
145 #define CHECK_CHAR( d ) ( temp = (FT_Char)d, (FT_Int) temp == (FT_Int) d )
146 #define CHECK_BYTE( d ) ( temp = (FT_Byte)d, (FT_UInt)temp == (FT_UInt)d )
147 #define CHECK_SHRT( d ) ( temp = (FT_Short)d, (FT_Int)temp == (FT_Int) d )
149 /* horizontal advance in pixels */
150 xadvance = ( slot->advance.x + 32 ) >> 6;
151 yadvance = ( slot->advance.y + 32 ) >> 6;
153 if ( !CHECK_BYTE( bitmap->rows ) ||
154 !CHECK_BYTE( bitmap->width ) ||
155 !CHECK_SHRT( bitmap->pitch ) ||
156 !CHECK_CHAR( slot->bitmap_left ) ||
157 !CHECK_CHAR( slot->bitmap_top ) ||
158 !CHECK_CHAR( xadvance ) ||
159 !CHECK_CHAR( yadvance ) )
161 FT_TRACE2(( "ftc_snode_load:"
162 " glyph too large for small bitmap cache\n"));
166 sbit->width = (FT_Byte)bitmap->width;
167 sbit->height = (FT_Byte)bitmap->rows;
168 sbit->pitch = (FT_Short)bitmap->pitch;
169 sbit->left = (FT_Char)slot->bitmap_left;
170 sbit->top = (FT_Char)slot->bitmap_top;
171 sbit->xadvance = (FT_Char)xadvance;
172 sbit->yadvance = (FT_Char)yadvance;
173 sbit->format = (FT_Byte)bitmap->pixel_mode;
174 sbit->max_grays = (FT_Byte)( bitmap->num_grays - 1 );
176 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
178 /* take the bitmap ownership */
179 sbit->buffer = bitmap->buffer;
180 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
184 /* copy the bitmap into a new buffer -- ignore error */
185 error = ftc_sbit_copy_bitmap( sbit, bitmap, manager->memory );
188 /* now, compute size */
190 *asize = (FT_ULong)FT_ABS( sbit->pitch ) * sbit->height;
192 } /* glyph loading successful */
194 /* ignore the errors that might have occurred -- */
195 /* we mark unloaded glyphs with `sbit.buffer == 0' */
196 /* and `width == 255', `height == 0' */
198 if ( error && FT_ERR_NEQ( error, Out_Of_Memory ) )
213 FT_LOCAL_DEF( FT_Error )
214 FTC_SNode_New( FTC_SNode *psnode,
218 FT_Memory memory = cache->memory;
220 FTC_SNode snode = NULL;
221 FT_UInt gindex = gquery->gindex;
222 FTC_Family family = gquery->family;
224 FTC_SFamilyClass clazz = FTC_CACHE_SFAMILY_CLASS( cache );
229 total = clazz->family_get_count( family, cache->manager );
230 if ( total == 0 || gindex >= total )
232 error = FT_THROW( Invalid_Argument );
236 if ( !FT_QNEW( snode ) )
238 FT_UInt count, start;
241 start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE );
242 count = total - start;
243 if ( count > FTC_SBIT_ITEMS_PER_NODE )
244 count = FTC_SBIT_ITEMS_PER_NODE;
246 FTC_GNode_Init( FTC_GNODE( snode ), start, family );
248 snode->count = count;
249 for ( node_count = 0; node_count < count; node_count++ )
251 snode->sbits[node_count].width = 255;
252 snode->sbits[node_count].height = 0;
253 snode->sbits[node_count].buffer = NULL;
256 error = ftc_snode_load( snode,
262 FTC_SNode_Free( snode, cache );
273 FT_LOCAL_DEF( FT_Error )
274 ftc_snode_new( FTC_Node *ftcpsnode,
275 FT_Pointer ftcgquery,
278 FTC_SNode *psnode = (FTC_SNode*)ftcpsnode;
279 FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
282 return FTC_SNode_New( psnode, gquery, cache );
286 FT_LOCAL_DEF( FT_Offset )
287 ftc_snode_weight( FTC_Node ftcsnode,
290 FTC_SNode snode = (FTC_SNode)ftcsnode;
291 FT_UInt count = snode->count;
292 FTC_SBit sbit = snode->sbits;
299 FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE );
301 /* the node itself */
302 size = sizeof ( *snode );
304 for ( ; count > 0; count--, sbit++ )
312 /* add the size of a given glyph image */
313 size += (FT_Offset)pitch * sbit->height;
323 FT_LOCAL_DEF( FT_Offset )
324 FTC_SNode_Weight( FTC_SNode snode )
326 return ftc_snode_weight( FTC_NODE( snode ), NULL );
332 FT_LOCAL_DEF( FT_Bool )
333 ftc_snode_compare( FTC_Node ftcsnode,
334 FT_Pointer ftcgquery,
336 FT_Bool* list_changed )
338 FTC_SNode snode = (FTC_SNode)ftcsnode;
339 FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
340 FTC_GNode gnode = FTC_GNODE( snode );
341 FT_UInt gindex = gquery->gindex;
346 *list_changed = FALSE;
347 result = FT_BOOL( gnode->family == gquery->family &&
348 gindex - gnode->gindex < snode->count );
351 /* check if we need to load the glyph bitmap now */
352 FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex );
356 * The following code illustrates what to do when you want to
357 * perform operations that may fail within a lookup function.
359 * Here, we want to load a small bitmap on-demand; we thus
360 * need to call the `ftc_snode_load' function which may return
361 * a non-zero error code only when we are out of memory (OOM).
363 * The correct thing to do is to use @FTC_CACHE_TRYLOOP and
364 * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop
365 * that is capable of flushing the cache incrementally when
366 * an OOM errors occur.
368 * However, we need to `lock' the node before this operation to
369 * prevent it from being flushed within the loop.
371 * When we exit the loop, we unlock the node, then check the `error'
372 * variable. If it is non-zero, this means that the cache was
373 * completely flushed and that no usable memory was found to load
376 * We then prefer to return a value of 0 (i.e., NO MATCH). This
377 * ensures that the caller will try to allocate a new node.
378 * This operation consequently _fail_ and the lookup function
379 * returns the appropriate OOM error code.
381 * Note that `buffer == NULL && width == 255' is a hack used to
382 * tag `unavailable' bitmaps in the array. We should never try
387 if ( !sbit->buffer && sbit->width == 255 )
393 ftcsnode->ref_count++; /* lock node to prevent flushing */
396 FTC_CACHE_TRYLOOP( cache )
398 error = ftc_snode_load( snode, cache->manager, gindex, &size );
400 FTC_CACHE_TRYLOOP_END( list_changed )
402 ftcsnode->ref_count--; /* unlock the node */
407 cache->manager->cur_weight += size;