1 /**************************************************************************** 2 * 3 * ftcsbits.c 4 * 5 * FreeType sbits manager (body). 6 * 7 * Copyright (C) 2000-2021 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_NEW( 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 } 253 254 error = ftc_snode_load( snode, 255 cache->manager, 256 gindex, 257 NULL ); 258 if ( error ) 259 { 260 FTC_SNode_Free( snode, cache ); 261 snode = NULL; 262 } 263 } 264 265 Exit: 266 *psnode = snode; 267 return error; 268 } 269 270 271 FT_LOCAL_DEF( FT_Error ) ftc_snode_new(FTC_Node * ftcpsnode,FT_Pointer ftcgquery,FTC_Cache cache)272 ftc_snode_new( FTC_Node *ftcpsnode, 273 FT_Pointer ftcgquery, 274 FTC_Cache cache ) 275 { 276 FTC_SNode *psnode = (FTC_SNode*)ftcpsnode; 277 FTC_GQuery gquery = (FTC_GQuery)ftcgquery; 278 279 280 return FTC_SNode_New( psnode, gquery, cache ); 281 } 282 283 284 FT_LOCAL_DEF( FT_Offset ) ftc_snode_weight(FTC_Node ftcsnode,FTC_Cache cache)285 ftc_snode_weight( FTC_Node ftcsnode, 286 FTC_Cache cache ) 287 { 288 FTC_SNode snode = (FTC_SNode)ftcsnode; 289 FT_UInt count = snode->count; 290 FTC_SBit sbit = snode->sbits; 291 FT_Int pitch; 292 FT_Offset size; 293 294 FT_UNUSED( cache ); 295 296 297 FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE ); 298 299 /* the node itself */ 300 size = sizeof ( *snode ); 301 302 for ( ; count > 0; count--, sbit++ ) 303 { 304 if ( sbit->buffer ) 305 { 306 pitch = sbit->pitch; 307 if ( pitch < 0 ) 308 pitch = -pitch; 309 310 /* add the size of a given glyph image */ 311 size += (FT_Offset)pitch * sbit->height; 312 } 313 } 314 315 return size; 316 } 317 318 319 #if 0 320 321 FT_LOCAL_DEF( FT_Offset ) 322 FTC_SNode_Weight( FTC_SNode snode ) 323 { 324 return ftc_snode_weight( FTC_NODE( snode ), NULL ); 325 } 326 327 #endif /* 0 */ 328 329 330 FT_LOCAL_DEF( FT_Bool ) ftc_snode_compare(FTC_Node ftcsnode,FT_Pointer ftcgquery,FTC_Cache cache,FT_Bool * list_changed)331 ftc_snode_compare( FTC_Node ftcsnode, 332 FT_Pointer ftcgquery, 333 FTC_Cache cache, 334 FT_Bool* list_changed ) 335 { 336 FTC_SNode snode = (FTC_SNode)ftcsnode; 337 FTC_GQuery gquery = (FTC_GQuery)ftcgquery; 338 FTC_GNode gnode = FTC_GNODE( snode ); 339 FT_UInt gindex = gquery->gindex; 340 FT_Bool result; 341 342 343 if (list_changed) 344 *list_changed = FALSE; 345 result = FT_BOOL( gnode->family == gquery->family && 346 gindex - gnode->gindex < snode->count ); 347 if ( result ) 348 { 349 /* check if we need to load the glyph bitmap now */ 350 FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex ); 351 352 353 /* 354 * The following code illustrates what to do when you want to 355 * perform operations that may fail within a lookup function. 356 * 357 * Here, we want to load a small bitmap on-demand; we thus 358 * need to call the `ftc_snode_load' function which may return 359 * a non-zero error code only when we are out of memory (OOM). 360 * 361 * The correct thing to do is to use @FTC_CACHE_TRYLOOP and 362 * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop 363 * that is capable of flushing the cache incrementally when 364 * an OOM errors occur. 365 * 366 * However, we need to `lock' the node before this operation to 367 * prevent it from being flushed within the loop. 368 * 369 * When we exit the loop, we unlock the node, then check the `error' 370 * variable. If it is non-zero, this means that the cache was 371 * completely flushed and that no usable memory was found to load 372 * the bitmap. 373 * 374 * We then prefer to return a value of 0 (i.e., NO MATCH). This 375 * ensures that the caller will try to allocate a new node. 376 * This operation consequently _fail_ and the lookup function 377 * returns the appropriate OOM error code. 378 * 379 * Note that `buffer == NULL && width == 255' is a hack used to 380 * tag `unavailable' bitmaps in the array. We should never try 381 * to load these. 382 * 383 */ 384 385 if ( !sbit->buffer && sbit->width == 255 ) 386 { 387 FT_ULong size; 388 FT_Error error; 389 390 391 ftcsnode->ref_count++; /* lock node to prevent flushing */ 392 /* in retry loop */ 393 394 FTC_CACHE_TRYLOOP( cache ) 395 { 396 error = ftc_snode_load( snode, cache->manager, gindex, &size ); 397 } 398 FTC_CACHE_TRYLOOP_END( list_changed ) 399 400 ftcsnode->ref_count--; /* unlock the node */ 401 402 if ( error ) 403 result = 0; 404 else 405 cache->manager->cur_weight += size; 406 } 407 } 408 409 return result; 410 } 411 412 413 #ifdef FTC_INLINE 414 415 FT_LOCAL_DEF( FT_Bool ) FTC_SNode_Compare(FTC_SNode snode,FTC_GQuery gquery,FTC_Cache cache,FT_Bool * list_changed)416 FTC_SNode_Compare( FTC_SNode snode, 417 FTC_GQuery gquery, 418 FTC_Cache cache, 419 FT_Bool* list_changed ) 420 { 421 return ftc_snode_compare( FTC_NODE( snode ), gquery, 422 cache, list_changed ); 423 } 424 425 #endif 426 427 /* END */ 428