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