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
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 )
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 )
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
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 )
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 )
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 )
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 )
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