1 /**************************************************************************** 2 * 3 * ftcsbits.c 4 * 5 * FreeType sbits manager (body). 6 * 7 * Copyright (C) 2000-2023 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 ftc_sbit_copy_bitmap(FTC_SBit sbit,FT_Bitmap * bitmap,FT_Memory memory)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 if ( !FT_QALLOC( sbit->buffer, size ) ) 57 FT_MEM_COPY( sbit->buffer, bitmap->buffer, size ); 58 59 return error; 60 } 61 62 63 FT_LOCAL_DEF( void ) ftc_snode_free(FTC_Node ftcsnode,FTC_Cache cache)64 ftc_snode_free( FTC_Node ftcsnode, 65 FTC_Cache cache ) 66 { 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; 71 72 73 for ( ; count > 0; sbit++, count-- ) 74 FT_FREE( sbit->buffer ); 75 76 FTC_GNode_Done( FTC_GNODE( snode ), cache ); 77 78 FT_FREE( snode ); 79 } 80 81 82 FT_LOCAL_DEF( void ) FTC_SNode_Free(FTC_SNode snode,FTC_Cache cache)83 FTC_SNode_Free( FTC_SNode snode, 84 FTC_Cache cache ) 85 { 86 ftc_snode_free( FTC_NODE( snode ), cache ); 87 } 88 89 90 /* 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. 96 * 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. 99 */ 100 static FT_Error ftc_snode_load(FTC_SNode snode,FTC_Manager manager,FT_UInt gindex,FT_ULong * asize)101 ftc_snode_load( FTC_SNode snode, 102 FTC_Manager manager, 103 FT_UInt gindex, 104 FT_ULong *asize ) 105 { 106 FT_Error error; 107 FTC_GNode gnode = FTC_GNODE( snode ); 108 FTC_Family family = gnode->family; 109 FT_Face face; 110 FTC_SBit sbit; 111 FTC_SFamilyClass clazz; 112 113 114 if ( gindex - gnode->gindex >= snode->count ) 115 { 116 FT_ERROR(( "ftc_snode_load: invalid glyph index" )); 117 return FT_THROW( Invalid_Argument ); 118 } 119 120 sbit = snode->sbits + ( gindex - gnode->gindex ); 121 clazz = (FTC_SFamilyClass)family->clazz; 122 123 error = clazz->family_load_glyph( family, gindex, manager, &face ); 124 if ( error ) 125 goto BadGlyph; 126 127 { 128 FT_Int temp; 129 FT_GlyphSlot slot = face->glyph; 130 FT_Bitmap* bitmap = &slot->bitmap; 131 FT_Pos xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */ 132 133 134 if ( slot->format != FT_GLYPH_FORMAT_BITMAP ) 135 { 136 FT_TRACE0(( "ftc_snode_load:" 137 " glyph loaded didn't return a bitmap\n" )); 138 goto BadGlyph; 139 } 140 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 */ 144 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 ) 148 149 /* horizontal advance in pixels */ 150 xadvance = ( slot->advance.x + 32 ) >> 6; 151 yadvance = ( slot->advance.y + 32 ) >> 6; 152 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 ) ) 160 { 161 FT_TRACE2(( "ftc_snode_load:" 162 " glyph too large for small bitmap cache\n")); 163 goto BadGlyph; 164 } 165 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 ); 175 176 if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) 177 { 178 /* take the bitmap ownership */ 179 sbit->buffer = bitmap->buffer; 180 slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; 181 } 182 else 183 { 184 /* copy the bitmap into a new buffer -- ignore error */ 185 error = ftc_sbit_copy_bitmap( sbit, bitmap, manager->memory ); 186 } 187 188 /* now, compute size */ 189 if ( asize ) 190 *asize = (FT_ULong)FT_ABS( sbit->pitch ) * sbit->height; 191 192 } /* glyph loading successful */ 193 194 /* ignore the errors that might have occurred -- */ 195 /* we mark unloaded glyphs with `sbit.buffer == 0' */ 196 /* and `width == 255', `height == 0' */ 197 /* */ 198 if ( error && FT_ERR_NEQ( error, Out_Of_Memory ) ) 199 { 200 BadGlyph: 201 sbit->width = 255; 202 sbit->height = 0; 203 sbit->buffer = NULL; 204 error = FT_Err_Ok; 205 if ( asize ) 206 *asize = 0; 207 } 208 209 return error; 210 } 211 212 213 FT_LOCAL_DEF( FT_Error ) FTC_SNode_New(FTC_SNode * psnode,FTC_GQuery gquery,FTC_Cache cache)214 FTC_SNode_New( FTC_SNode *psnode, 215 FTC_GQuery gquery, 216 FTC_Cache cache ) 217 { 218 FT_Memory memory = cache->memory; 219 FT_Error error; 220 FTC_SNode snode = NULL; 221 FT_UInt gindex = gquery->gindex; 222 FTC_Family family = gquery->family; 223 224 FTC_SFamilyClass clazz = FTC_CACHE_SFAMILY_CLASS( cache ); 225 FT_UInt total; 226 FT_UInt node_count; 227 228 229 total = clazz->family_get_count( family, cache->manager ); 230 if ( total == 0 || gindex >= total ) 231 { 232 error = FT_THROW( Invalid_Argument ); 233 goto Exit; 234 } 235 236 if ( !FT_QNEW( snode ) ) 237 { 238 FT_UInt count, start; 239 240 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; 245 246 FTC_GNode_Init( FTC_GNODE( snode ), start, family ); 247 248 snode->count = count; 249 for ( node_count = 0; node_count < count; node_count++ ) 250 { 251 snode->sbits[node_count].width = 255; 252 snode->sbits[node_count].height = 0; 253 snode->sbits[node_count].buffer = NULL; 254 } 255 256 error = ftc_snode_load( snode, 257 cache->manager, 258 gindex, 259 NULL ); 260 if ( error ) 261 { 262 FTC_SNode_Free( snode, cache ); 263 snode = NULL; 264 } 265 } 266 267 Exit: 268 *psnode = snode; 269 return error; 270 } 271 272 273 FT_LOCAL_DEF( FT_Error ) ftc_snode_new(FTC_Node * ftcpsnode,FT_Pointer ftcgquery,FTC_Cache cache)274 ftc_snode_new( FTC_Node *ftcpsnode, 275 FT_Pointer ftcgquery, 276 FTC_Cache cache ) 277 { 278 FTC_SNode *psnode = (FTC_SNode*)ftcpsnode; 279 FTC_GQuery gquery = (FTC_GQuery)ftcgquery; 280 281 282 return FTC_SNode_New( psnode, gquery, cache ); 283 } 284 285 286 FT_LOCAL_DEF( FT_Offset ) ftc_snode_weight(FTC_Node ftcsnode,FTC_Cache cache)287 ftc_snode_weight( FTC_Node ftcsnode, 288 FTC_Cache cache ) 289 { 290 FTC_SNode snode = (FTC_SNode)ftcsnode; 291 FT_UInt count = snode->count; 292 FTC_SBit sbit = snode->sbits; 293 FT_Int pitch; 294 FT_Offset size; 295 296 FT_UNUSED( cache ); 297 298 299 FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE ); 300 301 /* the node itself */ 302 size = sizeof ( *snode ); 303 304 for ( ; count > 0; count--, sbit++ ) 305 { 306 if ( sbit->buffer ) 307 { 308 pitch = sbit->pitch; 309 if ( pitch < 0 ) 310 pitch = -pitch; 311 312 /* add the size of a given glyph image */ 313 size += (FT_Offset)pitch * sbit->height; 314 } 315 } 316 317 return size; 318 } 319 320 321 #if 0 322 323 FT_LOCAL_DEF( FT_Offset ) 324 FTC_SNode_Weight( FTC_SNode snode ) 325 { 326 return ftc_snode_weight( FTC_NODE( snode ), NULL ); 327 } 328 329 #endif /* 0 */ 330 331 332 FT_LOCAL_DEF( FT_Bool ) ftc_snode_compare(FTC_Node ftcsnode,FT_Pointer ftcgquery,FTC_Cache cache,FT_Bool * list_changed)333 ftc_snode_compare( FTC_Node ftcsnode, 334 FT_Pointer ftcgquery, 335 FTC_Cache cache, 336 FT_Bool* list_changed ) 337 { 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; 342 FT_Bool result; 343 344 345 if ( list_changed ) 346 *list_changed = FALSE; 347 result = FT_BOOL( gnode->family == gquery->family && 348 gindex - gnode->gindex < snode->count ); 349 if ( result ) 350 { 351 /* check if we need to load the glyph bitmap now */ 352 FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex ); 353 354 355 /* 356 * The following code illustrates what to do when you want to 357 * perform operations that may fail within a lookup function. 358 * 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). 362 * 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. 367 * 368 * However, we need to `lock' the node before this operation to 369 * prevent it from being flushed within the loop. 370 * 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 374 * the bitmap. 375 * 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. 380 * 381 * Note that `buffer == NULL && width == 255' is a hack used to 382 * tag `unavailable' bitmaps in the array. We should never try 383 * to load these. 384 * 385 */ 386 387 if ( !sbit->buffer && sbit->width == 255 ) 388 { 389 FT_ULong size; 390 FT_Error error; 391 392 393 ftcsnode->ref_count++; /* lock node to prevent flushing */ 394 /* in retry loop */ 395 396 FTC_CACHE_TRYLOOP( cache ) 397 { 398 error = ftc_snode_load( snode, cache->manager, gindex, &size ); 399 } 400 FTC_CACHE_TRYLOOP_END( list_changed ) 401 402 ftcsnode->ref_count--; /* unlock the node */ 403 404 if ( error ) 405 result = 0; 406 else 407 cache->manager->cur_weight += size; 408 } 409 } 410 411 return result; 412 } 413 414 /* END */ 415