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