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