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