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