freetype-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [ft-devel] limiting the heap memory used by a TrueType font


From: Graham Asher
Subject: RE: [ft-devel] limiting the heap memory used by a TrueType font
Date: Fri, 14 Dec 2007 14:46:38 -0000

David,

 

I realise that in the case of memory-mapped data this is not needed (you speak to one who has been single-stepping through the code, and modifying it). I should have clarified my first message by explaining that I am working with a platform where the data is stored in segmented form in Flash memory that is subject to relocation, and thus, although the stream interface works very fast, because it has access to the pointers to the data segments, it cannot be dispensed with. I expect that this is a common situation.

 

However, what I have done is essential for the platform I am working on, and may well be essential for other low-RAM devices. In my current work we can use large TrueType fonts (~2BM Japanese fonts, for example) successfully with my changes; we cannot use them without my changes. Also, my CMAP code is entirely non-invasive; it doesn’t change the FreeType code or API at all but is an optional add-on. (You call a function that gets the memory offset of the charmap, stores it, then unloads the CMAP table; then you use a replacement function instead of FT_Get_Char_Index.)

 

Note also that what I propose is optional; and can be tuned to affect CMAP, LOCA, etc., tables above a certain size only; and if made a run-time option rather than a compile-time option (I mentioned this in my first message), can be made to work only for non-memory-mapped files.

 

I don’t think making FT_Get_Char_Index use a stream interface would cripple text layout performance. I have no benchmarking to back that assertion up yet, I admit, but my experience when profiling is that TrueType rendering is dominated by rasterization. The stream interface will often (as is the case with my current work) have access to the data in the form of a series of discontinuous memory blocks; that is, the file is memory-mapped but segmented and thus requires a stream interface, but the overhead is relatively low because the data is actually in memory.

 

Further, getting a glyph location from the LOCA table requires a only one seek and two reads; there is no very good reason for copying the LOCA table into RAM.

 

Here is a working and tested alternative version of tt_face_get_location; in this code, the pointer face->glyph_locations has been replaced by an offset face->glyph_locations_offset.

 

  FT_LOCAL_DEF( FT_ULong )

  tt_face_get_location( TT_Face   face,

                        FT_UInt   gindex,

                        FT_UInt  *asize )

  {

    FT_ULong  pos1, pos2;

    FT_ULong  p;

    FT_ULong  p_limit;

    FT_Stream  stream = face->root.stream;

    FT_Error error = 0;

 

    pos1 = pos2 = 0;

 

    if ( gindex < face->num_locations )

    {

      if ( face->header.Index_To_Loc_Format != 0 )

      {

        p       = face->glyph_locations_offset + gindex * 4;

        p_limit = face->glyph_locations_offset + face->num_locations * 4;

                        error = FT_Stream_Seek(stream,p);

                        pos1 = FT_Stream_ReadLong(stream,&error);

        pos2 = pos1;

 

        if ( p + 4 <= p_limit )

          pos2 = FT_Stream_ReadLong(stream,&error);

      }

      else

      {

        p       = face->glyph_locations_offset + gindex * 2;

        p_limit = face->glyph_locations_offset + face->num_locations * 2;

                        error = FT_Stream_Seek(stream,p);

                        pos1 = FT_Stream_ReadShort(stream,&error) & 0xFFFF;

        pos2 = pos1;

 

        if ( p + 2 <= p_limit )

          pos2 = FT_Stream_ReadShort(stream,&error) & 0xFFFF;

 

        pos1 <<= 1;

        pos2 <<= 1;

      }

    }

 

    /* It isn't mentioned explicitly that the `loca' table must be  */

    /* ordered, but implicitly it refers to the length of an entry  */

    /* as the difference between the current and the next position. */

    /* Anyway, there do exist (malformed) fonts which don't obey    */

    /* this rule, so we are only able to provide an upper bound for */

    /* the size.                                                    */

    if ( pos2 >= pos1 )

      *asize = (FT_UInt)( pos2 - pos1 );

    else

      *asize = (FT_UInt)( face->glyf_len - pos1 );

 

    return pos1;

  }

 

In conclusion I believe that what I have done is not only useful – in fact, essential –  to my current project but of potential use to FreeType users on other devices with limited heap memory.

 

Best regards,

 

Graham

 

 


From: address@hidden [mailto:address@hidden On Behalf Of David Turner
Sent: 14 December 2007 14:17
To: Graham Asher
Cc: address@hidden
Subject: Re: [ft-devel] limiting the heap memory used by a TrueType font

 

Hi Graham,

first, keep in mind that the 'loca' table, as well as many other ones, are simply never copied in the heap if the font file is already memory-mapped.
this happens if you use FT_New_Memory_Face, or even FT_New_Face if your underlying stream implementation supports memory-mapped files (the Linux one does). in these cases, all accesses are directly done from the original font file memory. see the code in FT_Stream_ExtractFrame for details.

FT_Get_Char_Index is one of these functions that are heavily used by a typical text rendering engine that uses FreeType, and modifying the underlying cmap decoders to use a stream interface is certainly possible, but is going to have a significant impact on text layout performance.

- David

2007/12/11, Graham Asher <address@hidden>:

TrueType fonts contain several tables that vary in a linear way with the
size of the font. Some of these, for example hmtx (horizontal metrics) are
not loaded into the heap, but are accessed using the stream interface.
Others, like cmap (which maps character codes to glyph indexes) and loca
(which maps glyph indexes to glyph data) are loaded in their entirety into
the heap.

The fact that some of these tables are loaded into the heap means that for
devices with a small amount of RAM there is always a font big enough to
cause an out-of-memory error; the amount of heap used is O(N) where N is the
number of glyphs. The font itself can be kept 'somewhere else' - in ROM, for
instance - so the size of the font data itself is not at issue. On small
devices RAM is typically in much shorter supply than ROM.

An ideal solution to the problem is to make it a configurable option for all
tables to be accessed via the stream interface and not loaded into the heap.
I have already experimentally done this for the cmap table and it works
perfectly, saving over 80K when loading a Japanese font. The technique is
this:

1. Allow the cmap table to be loaded as normal.

2. Note its location in the file, and any other information needed to access
it.

3. Unload the cmap table from the heap.

4. Replace FT_Get_Char_Index with a new function that reads the cmap via a
stream interface.

For the 'loca' table the only good way to prevent it from being loaded is to
unload it then override the normal way of getting glyph data, using the
incremental interface; however, it would be much nicer if there was a
compile-time or perhaps run-time option that forced stream access to all
variable-sized tables.

I have had time to do this only with rather platform-specific code which
unfortunately I can't show here because it is proprietary, but I just wanted
to give my opinion that this would be a useful new feature for FreeType. I
shall very probably not have time to do it myself, but someone more familiar
with FreeType internals might be able to code it up very quickly in a way
that fitted in with the existing architecture.

Best regards,

Graham Asher




_______________________________________________
Freetype-devel mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/freetype-devel

 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]