shithub: freetype+ttf2subf

Download patch

ref: c1b6d08291087287bceb4dc2ec2c82a295adc575
parent: 7e30776ee8b96dd2d5a9646db5a4ed3356690447
author: David Turner <[email protected]>
date: Thu Oct 20 11:33:34 EDT 2005

* src/base/ftdbgmem.c: fixes to better account for memory reallocations

  * src/lzw/ftlzw2.c, src/lzw/ftzopen.h, src/lzw/ftzopen.c, src/lzw/rules.mk:
    first version of LZW loader re-implementation. Apparently, saves about
    260 KB of heap memory when loading tir24.pcf.Z

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-10-20  David Turner  <[email protected]>
+
+  * src/base/ftdbgmem.c: fixes to better account for memory reallocations
+
+  * src/lzw/ftlzw2.c, src/lzw/ftzopen.h, src/lzw/ftzopen.c, src/lzw/rules.mk:
+    first version of LZW loader re-implementation. Apparently, saves about
+    260 KB of heap memory when loading timR24.pcf.Z
+
 2005-10-20  Chia-I Wu  <[email protected]>
 
 	* include/freetype/ftbitmap.h (FT_Bitmap_Copy, FT_Bitmap_Embolden),
--- a/src/base/ftdbgmem.c
+++ b/src/base/ftdbgmem.c
@@ -493,7 +493,8 @@
   static void
   ft_mem_table_set( FT_MemTable  table,
                     FT_Byte*     address,
-                    FT_ULong     size )
+                    FT_ULong     size,
+                    FT_Long      delta )
   {
     FT_MemNode  *pnode, node;
 
@@ -536,19 +537,33 @@
 
       node->address = address;
       node->size    = size;
+      node->source  = source = ft_mem_table_get_source( table );
 
-      node->source = source = ft_mem_table_get_source( table );
+      if ( delta == 0 )
+      {
+        /* this is an allocation */
+        source->all_blocks++;
+        source->cur_blocks++;
+        if ( source->cur_blocks > source->max_blocks )
+          source->max_blocks = source->cur_blocks;
+      }
 
-      source->all_blocks++;
-      source->cur_blocks++;
-      if ( source->cur_blocks > source->max_blocks )
-        source->max_blocks = source->cur_blocks;
-
       if ( size > (FT_ULong)source->cur_max )
         source->cur_max = size;
 
+      if ( delta != 0 )
+      {
+        /* we're growing or shrinking a realloc-ed block */
+        source->cur_size += delta;
+      }
+      else
+      {
+        /* we're allocating a new block */
+        source->cur_size += size;
+      }
+
       source->all_size += size;
-      source->cur_size += size;
+
       if ( source->cur_size > source->max_size )
         source->max_size = source->cur_size;
 
@@ -560,8 +575,16 @@
       pnode[0] = node;
       table->nodes++;
 
-      table->alloc_total   += size;
-      table->alloc_current += size;
+      if ( delta != 0 )
+      {
+        table->alloc_total   += size;
+        table->alloc_current += delta;
+      }
+      else
+      {
+        table->alloc_total   += size;
+        table->alloc_current += size;
+      }
       if ( table->alloc_current > table->alloc_max )
         table->alloc_max = table->alloc_current;
 
@@ -574,7 +597,8 @@
 
   static void
   ft_mem_table_remove( FT_MemTable  table,
-                       FT_Byte*     address )
+                       FT_Byte*     address,
+                       FT_Long      delta )
   {
     if ( table )
     {
@@ -599,13 +623,17 @@
 
         /* scramble the node's content for additional safety */
         FT_MEM_SET( address, 0xF3, node->size );
-        table->alloc_current -= node->size;
 
-        source = node->source;
+        if ( delta == 0 )
+        {
+          source = node->source;
 
-        source->cur_blocks--;
-        source->cur_size -= node->size;
+          source->cur_blocks--;
+          source->cur_size -= node->size;
 
+          table->alloc_current -= node->size;
+        }
+
         if ( table->keep_alive )
         {
           /* we simply invert the node's size to indicate that the node */
@@ -662,9 +690,11 @@
 
     block = (FT_Byte *)ft_mem_table_alloc( table, size );
     if ( block )
-      ft_mem_table_set( table, block, (FT_ULong)size );
+    {
+      ft_mem_table_set( table, block, (FT_ULong)size, 0 );
 
-    table->alloc_count++;
+      table->alloc_count++;
+    }
 
     table->file_name = NULL;
     table->line_no   = 0;
@@ -685,7 +715,7 @@
                           FT_FILENAME( table->file_name ),
                           table->line_no );
 
-    ft_mem_table_remove( table, (FT_Byte*)block );
+    ft_mem_table_remove( table, (FT_Byte*)block, 0 );
 
     if ( !table->keep_alive )
       ft_mem_table_free( table, block );
@@ -706,6 +736,7 @@
     FT_MemTable  table = (FT_MemTable)memory->user;
     FT_MemNode   node, *pnode;
     FT_Pointer   new_block;
+    FT_Long      delta;
 
     const char*  file_name = FT_FILENAME( table->file_name );
     FT_Long      line_no   = table->line_no;
@@ -743,6 +774,7 @@
                           "%ld instead of %ld in (%s:%ld)",
                           block, cur_size, node->size, file_name, line_no );
 
+#if 0
     new_block = ft_mem_debug_alloc( memory, new_size );
     if ( new_block == NULL )
       return NULL;
@@ -753,6 +785,43 @@
     table->line_no   = line_no;
 
     ft_mem_debug_free( memory, (FT_Byte*)block );
+
+#else
+    /* return NULL if the maximum number of allocations was reached */
+    if ( table->bound_count                           &&
+         table->alloc_count >= table->alloc_count_max )
+      return NULL;
+
+    delta = (FT_Long)( new_size - cur_size );
+
+    /* return NULL if this allocation would overflow the maximum heap size */
+    if ( delta > 0                                                       &&
+         table->bound_total                                              &&
+         table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
+      return NULL;
+
+    new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
+    if ( new_block == NULL )
+      return NULL;
+
+    ft_mem_table_set( table, new_block, new_size, delta );
+
+    table->file_name = NULL;
+    table->line_no   = 0;
+
+    ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
+
+    table->file_name = file_name;
+    table->line_no   = line_no;
+
+    ft_mem_table_remove( table, (FT_Byte*)block, delta );
+
+    if ( !table->keep_alive )
+      ft_mem_table_free( table, block );
+
+    table->alloc_current += delta;
+
+#endif
 
     return new_block;
   }
--- /dev/null
+++ b/src/lzw/ftlzw2.c
@@ -1,0 +1,411 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ftlzw.c                                                                */
+/*                                                                         */
+/*    FreeType support for .Z compressed files.                            */
+/*                                                                         */
+/*  This optional component relies on NetBSD's zopen().  It should mainly  */
+/*  be used to parse compressed PCF fonts, as found with many X11 server   */
+/*  distributions.                                                         */
+/*                                                                         */
+/*  Copyright 2004, 2005 by                                                */
+/*  Albert Chin-A-Young.                                                   */
+/*                                                                         */
+/*  Based on code in src/gzip/ftgzip.c, Copyright 2004 by                  */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+#include <string.h>
+#include <stdio.h>
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX  LZW_Err_
+#define FT_ERR_BASE    FT_Mod_Err_LZW
+
+#include FT_ERRORS_H
+
+
+#ifdef FT_CONFIG_OPTION_USE_LZW
+
+#include "ftzopen.h"
+
+
+/***************************************************************************/
+/***************************************************************************/
+/*****                                                                 *****/
+/*****                  M E M O R Y   M A N A G E M E N T              *****/
+/*****                                                                 *****/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+/***************************************************************************/
+/*****                                                                 *****/
+/*****                   F I L E   D E S C R I P T O R                 *****/
+/*****                                                                 *****/
+/***************************************************************************/
+/***************************************************************************/
+
+#define  FT_LZW_BUFFER_SIZE  4096
+
+  typedef struct FT_LZWFileRec_
+  {
+    FT_Stream       source;         /* parent/source stream        */
+    FT_Stream       stream;         /* embedding stream            */
+    FT_Memory       memory;         /* memory allocator            */
+    FT_LzwStateRec  lzw;            /* lzw decompressor state      */
+
+    FT_Byte         buffer[ FT_LZW_BUFFER_SIZE ]; /* output buffer */
+    FT_ULong        pos;                          /* position in output */
+    FT_Byte*        cursor;
+    FT_Byte*        limit;
+
+  } FT_LZWFileRec, *FT_LZWFile;
+
+
+  /* check and skip .Z header */
+  static FT_Error
+  ft_lzw_check_header( FT_Stream  stream )
+  {
+    FT_Error  error;
+    FT_Byte   head[2];
+
+
+    if ( FT_STREAM_SEEK( 0 )       ||
+         FT_STREAM_READ( head, 2 ) )
+      goto Exit;
+
+    /* head[0] && head[1] are the magic numbers     */
+    if ( head[0] != 0x1f ||
+         head[1] != 0x9d )
+      error = LZW_Err_Invalid_File_Format;
+
+  Exit:
+    return error;
+  }
+
+
+  static FT_Error
+  ft_lzw_file_init( FT_LZWFile  zip,
+                    FT_Stream   stream,
+                    FT_Stream   source )
+  {
+    FT_LzwState  lzw   = &zip->lzw;
+    FT_Error     error = LZW_Err_Ok;
+
+
+    zip->stream = stream;
+    zip->source = source;
+    zip->memory = stream->memory;
+
+    zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
+    zip->cursor = zip->limit;
+    zip->pos    = 0;
+
+    /* check and skip .Z header */
+    {
+      stream = source;
+
+      error = ft_lzw_check_header( source );
+      if ( error )
+        goto Exit;
+    }
+
+    /* initialize internal lzw variable */
+    ft_lzwstate_init( lzw, source );
+
+  Exit:
+    return error;
+  }
+
+
+  static void
+  ft_lzw_file_done( FT_LZWFile  zip )
+  {
+    /* clear the rest */
+    ft_lzwstate_done( &zip->lzw );
+
+    zip->memory = NULL;
+    zip->source = NULL;
+    zip->stream = NULL;
+  }
+
+
+  static FT_Error
+  ft_lzw_file_reset( FT_LZWFile  zip )
+  {
+    FT_Stream  stream = zip->source;
+    FT_Error   error;
+
+
+    if ( !FT_STREAM_SEEK( 0 ) )
+    {
+      ft_lzwstate_reset( &zip->lzw );
+
+      zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
+      zip->cursor = zip->limit;
+      zip->pos    = 0;
+    }
+
+    return error;
+  }
+
+
+  static FT_Error
+  ft_lzw_file_fill_output( FT_LZWFile  zip )
+  {
+    FT_LzwState  lzw = &zip->lzw;
+    FT_ULong     count;
+    FT_Error     error   = 0;
+
+
+    zip->cursor = zip->buffer;
+
+    count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE );
+
+    zip->limit = zip->cursor + count;
+
+    if ( count == 0 )
+      error = LZW_Err_Invalid_Stream_Operation;
+
+    return error;
+  }
+
+
+  /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */
+  static FT_Error
+  ft_lzw_file_skip_output( FT_LZWFile  zip,
+                           FT_ULong    count )
+  {
+    FT_Error  error = LZW_Err_Ok;
+
+   /* first, we skip what we can from the output buffer
+    */
+    {
+      FT_ULong  delta = (FT_ULong)( zip->limit - zip->cursor );
+
+      if ( delta >= count )
+        delta = count;
+
+      zip->cursor += delta;
+      zip->pos    += delta;
+
+      count -= delta;
+    }
+
+   /* next, we skip as many bytes remaining as possible
+    */
+    while ( count > 0 )
+    {
+      FT_ULong  delta = FT_LZW_BUFFER_SIZE;
+      FT_ULong  numread;
+
+      if ( delta > count )
+        delta = count;
+
+      numread = ft_lzwstate_io( &zip->lzw, NULL, delta );
+      if ( numread < delta )
+      {
+        /* not enough bytes */
+        error = LZW_Err_Invalid_Stream_Operation;
+        break;
+      }
+
+      zip->pos += delta;
+      count    -= delta;
+    }
+
+    return error;
+  }
+
+
+  static FT_ULong
+  ft_lzw_file_io( FT_LZWFile  zip,
+                  FT_ULong    pos,
+                  FT_Byte*    buffer,
+                  FT_ULong    count )
+  {
+    FT_ULong  result = 0;
+    FT_Error  error;
+
+
+    /* seeking backwards. */
+    if ( pos < zip->pos )
+    {
+     /* if the new position is within the output buffer, simply
+      * decrement pointers, otherwise, we'll reset the stream completely !!
+      */
+      if ( (zip->pos - pos) <= (FT_ULong)(zip->cursor - zip->buffer) )
+      {
+        zip->cursor -= (zip->pos - pos);
+        zip->pos     = pos;
+      }
+      else
+      {
+        error = ft_lzw_file_reset( zip );
+        if ( error )
+          goto Exit;
+      }
+    }
+
+    /* skip unwanted bytes */
+    if ( pos > zip->pos )
+    {
+      error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
+      if ( error )
+        goto Exit;
+    }
+
+    if ( count == 0 )
+      goto Exit;
+
+    /* now read the data */
+    for (;;)
+    {
+      FT_ULong  delta;
+
+
+      delta = (FT_ULong)( zip->limit - zip->cursor );
+      if ( delta >= count )
+        delta = count;
+
+      FT_MEM_COPY( buffer + result, zip->cursor, delta );
+      result      += delta;
+      zip->cursor += delta;
+      zip->pos    += delta;
+
+      count -= delta;
+      if ( count == 0 )
+        break;
+
+      error = ft_lzw_file_fill_output( zip );
+      if ( error )
+        break;
+    }
+
+  Exit:
+    return result;
+  }
+
+
+/***************************************************************************/
+/***************************************************************************/
+/*****                                                                 *****/
+/*****            L Z W   E M B E D D I N G   S T R E A M              *****/
+/*****                                                                 *****/
+/***************************************************************************/
+/***************************************************************************/
+
+  static void
+  ft_lzw_stream_close( FT_Stream  stream )
+  {
+    FT_LZWFile  zip    = (FT_LZWFile)stream->descriptor.pointer;
+    FT_Memory   memory = stream->memory;
+
+
+    if ( zip )
+    {
+      /* finalize lzw file descriptor */
+      ft_lzw_file_done( zip );
+
+      FT_FREE( zip );
+
+      stream->descriptor.pointer = NULL;
+    }
+  }
+
+
+  static FT_ULong
+  ft_lzw_stream_io( FT_Stream  stream,
+                    FT_ULong   pos,
+                    FT_Byte*   buffer,
+                    FT_ULong   count )
+  {
+    FT_LZWFile  zip = (FT_LZWFile)stream->descriptor.pointer;
+
+
+    return ft_lzw_file_io( zip, pos, buffer, count );
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stream_OpenLZW( FT_Stream  stream,
+                     FT_Stream  source )
+  {
+    FT_Error    error;
+    FT_Memory   memory = source->memory;
+    FT_LZWFile  zip;
+
+
+    /*
+     *  Check the header right now; this prevents allocation a huge
+     *  LZWFile object (400 KByte of heap memory) if not necessary.
+     *
+     *  Did I mention that you should never use .Z compressed font
+     *  file?
+     */
+    error = ft_lzw_check_header( source );
+    if ( error )
+      goto Exit;
+
+    FT_ZERO( stream );
+    stream->memory = memory;
+
+    if ( !FT_NEW( zip ) )
+    {
+      error = ft_lzw_file_init( zip, stream, source );
+      if ( error )
+      {
+        FT_FREE( zip );
+        goto Exit;
+      }
+
+      stream->descriptor.pointer = zip;
+    }
+
+    stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
+    stream->pos   = 0;
+    stream->base  = 0;
+    stream->read  = ft_lzw_stream_io;
+    stream->close = ft_lzw_stream_close;
+
+  Exit:
+    return error;
+  }
+
+#include "ftzopen.c"
+
+
+#else  /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stream_OpenLZW( FT_Stream  stream,
+                     FT_Stream  source )
+  {
+    FT_UNUSED( stream );
+    FT_UNUSED( source );
+
+    return LZW_Err_Unimplemented_Feature;
+  }
+
+
+#endif /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+/* END */
--- /dev/null
+++ b/src/lzw/ftzopen.c
@@ -1,0 +1,355 @@
+#include "ftzopen.h"
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+
+/* refill input buffer, return 0 on success, or -1 if eof
+ */
+static int
+ft_lzwstate_refill( FT_LzwState  state )
+{
+  int  result = -1;
+
+  if ( !state->in_eof )
+  {
+    FT_ULong  count = FT_Stream_TryRead( state->source,
+                                         state->in_buff,
+                                         sizeof( state->in_buff ) );
+
+    state->in_cursor = state->in_buff;
+    state->in_limit  = state->in_buff + count;
+    state->in_eof    = FT_BOOL( count < sizeof( state->in_buff ) );
+
+    if ( count > 0 )
+      result = 0;
+  }
+  return result;
+}
+
+
+/* return new code of 'num_bits', or -1 if eof
+ */
+static FT_Int32
+ft_lzwstate_get_code( FT_LzwState  state,
+                      FT_UInt      num_bits )
+{
+  FT_Int32   result   = -1;
+  FT_UInt32  pad      = state->pad;
+  FT_UInt    pad_bits = state->pad_bits;
+
+  while ( num_bits > pad_bits )
+  {
+    if ( state->in_cursor >= state->in_limit &&
+         ft_lzwstate_refill( state ) < 0    )
+      goto Exit;
+
+    pad      |= (FT_UInt32)(*state->in_cursor++) << pad_bits;
+    pad_bits += 8;
+  }
+
+  result          = (FT_Int32)( pad & LZW_MASK(num_bits) );
+  state->pad_bits = pad_bits - num_bits;
+  state->pad      = pad >> num_bits;
+
+Exit:
+  return result;
+}
+
+
+/* grow the character stack
+ */
+static int
+ft_lzwstate_stack_grow( FT_LzwState  state )
+{
+  if ( state->stack_top >= state->stack_size )
+  {
+    FT_Memory  memory = state->memory;
+    FT_Error   error;
+    FT_UInt    old_size = state->stack_size;
+    FT_UInt    new_size = old_size;
+
+    new_size = new_size + (new_size >> 1) + 4;
+
+    if ( state->stack == state->stack_0 )
+    {
+      state->stack = NULL;
+      old_size     = 0;
+    }
+
+    if ( FT_RENEW_ARRAY( state->stack, old_size, new_size ) )
+      return -1;
+
+    state->stack_size = new_size;
+  }
+  return 0;
+}
+
+
+/* grow the prefix/suffix arrays
+ */
+static int
+ft_lzwstate_prefix_grow( FT_LzwState  state )
+{
+  FT_UInt    old_size = state->prefix_size;
+  FT_UInt    new_size = old_size;
+  FT_Memory  memory   = state->memory;
+  FT_Error   error;
+
+  if ( new_size == 0 )  /* first allocation -> 9 bits */
+    new_size = 512;
+  else
+    new_size += (new_size >> 2);  /* don't grow too fast */
+
+ /* note that the 'suffix' array is located in the same memory block
+  * pointed to by 'prefix'
+  *
+  * I know that sizeof(FT_Byte) == 1 by definition, but it's clearer
+  * to write it literally
+  */
+  if ( FT_REALLOC( state->prefix,
+                   old_size*(sizeof(FT_UShort)+sizeof(FT_Byte)),
+                   new_size*(sizeof(FT_UShort)+sizeof(FT_Byte)) ) )
+    return -1;
+
+  /* now adjust 'suffix' and move the data accordingly */
+  state->suffix = (FT_Byte*)(state->prefix + new_size);
+
+  FT_MEM_MOVE( state->suffix,
+               state->prefix + old_size,
+               old_size * sizeof(FT_Byte) );
+
+  state->prefix_size = new_size;
+  return 0;
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_reset( FT_LzwState  state )
+{
+  state->in_cursor = state->in_buff;
+  state->in_limit  = state->in_buff;
+  state->in_eof    = 0;
+  state->pad_bits  = 0;
+  state->pad       = 0;
+
+  state->stack_top  = 0;
+  state->num_bits   = LZW_INIT_BITS;
+  state->phase      = FT_LZW_PHASE_START;
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_init( FT_LzwState  state,
+                   FT_Stream    source )
+{
+  FT_ZERO( state );
+
+  state->source = source;
+  state->memory = source->memory;
+
+  state->prefix      = NULL;
+  state->suffix      = NULL;
+  state->prefix_size = 0;
+
+  state->stack      = state->stack_0;
+  state->stack_size = sizeof(state->stack_0);
+
+  ft_lzwstate_reset( state );
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_done( FT_LzwState  state )
+{
+  FT_Memory  memory = state->memory;
+
+  ft_lzwstate_reset( state );
+
+  if ( state->stack != state->stack_0 )
+    FT_FREE( state->stack );
+
+  FT_FREE( state->prefix );
+  state->suffix = NULL;
+
+  FT_ZERO( state );
+}
+
+
+#define  FTLZW_STACK_PUSH(c)                               \
+  FT_BEGIN_STMNT                                           \
+    if ( state->stack_top >= state->stack_size &&          \
+         ft_lzwstate_stack_grow( state ) < 0   )           \
+      goto Eof;                                            \
+                                                           \
+    state->stack[ state->stack_top++ ] = (FT_Byte)(c);     \
+  FT_END_STMNT
+
+
+
+FT_LOCAL_DEF( FT_ULong )
+ft_lzwstate_io( FT_LzwState  state,
+                FT_Byte*     buffer,
+                FT_ULong     out_size )
+{
+  FT_ULong  result   = 0;
+
+  FT_UInt   num_bits = state->num_bits;
+  FT_UInt   free_ent = state->free_ent;
+  FT_UInt   old_char = state->old_char;
+  FT_UInt   old_code = state->old_code;
+  FT_UInt   in_code  = state->in_code;
+
+  if ( out_size == 0 )
+    goto Exit;
+
+  switch ( state->phase )
+  {
+    case FT_LZW_PHASE_START:
+      {
+        FT_Byte   max_bits;
+        FT_Int32  c;
+
+        /* skip magic bytes, and read max_bits + block_flag */
+        if ( FT_Stream_Seek( state->source, 2 ) != 0               ||
+             FT_Stream_TryRead( state->source, &max_bits, 1 ) != 1 )
+          goto Eof;
+
+        state->max_bits   = max_bits & LZW_BIT_MASK;
+        state->block_mode = max_bits & LZW_BLOCK_MASK;
+        state->max_free   = (FT_UInt)((1UL << state->max_bits) - 256);
+
+        if ( state->max_bits > LZW_MAX_BITS )
+          goto Eof;
+
+        num_bits = LZW_INIT_BITS;
+        free_ent = (state->block_mode ? LZW_FIRST : LZW_CLEAR) - 256;
+        in_code  = 0;
+
+        state->free_bits = num_bits < state->max_bits
+                         ? (FT_UInt)((1UL << num_bits) - 256)
+                         : state->max_free+1;
+
+        c = ft_lzwstate_get_code( state, num_bits );
+        if ( c < 0 )
+          goto Eof;
+
+        old_code = old_char = (FT_UInt)c;
+
+        if ( buffer )
+          buffer[result] = (FT_Byte)old_char;
+
+        if ( ++result >= out_size )
+          goto Exit;
+
+        state->phase = FT_LZW_PHASE_CODE;
+      }
+      /* fall-through */
+
+    case FT_LZW_PHASE_CODE:
+      {
+        FT_Int32  c;
+        FT_UInt   code;
+
+      NextCode:
+        c = ft_lzwstate_get_code( state, num_bits );
+        if ( c < 0 ) goto Eof;
+
+        code = (FT_UInt)c;
+
+        if ( code == LZW_CLEAR && state->block_mode )
+        {
+          free_ent = (LZW_FIRST-1)-256; /* why not LZW_FIRST-256 ? */
+          num_bits = LZW_INIT_BITS;
+
+          state->free_bits = num_bits < state->max_bits
+                           ? (FT_UInt)((1UL << num_bits) - 256)
+                           : state->max_free+1;
+
+          c = ft_lzwstate_get_code( state, num_bits );
+          if ( c < 0 ) goto Eof;
+
+          code = (FT_UInt)c;
+        }
+
+        in_code = code; /* save code for later */
+
+        if ( code >= 256U )
+        {
+          /* special case for KwKwKwK */
+          if ( code-256U >= free_ent )
+          {
+            FTLZW_STACK_PUSH( old_char );
+            code = old_code;
+          }
+
+          while ( code >= 256U )
+          {
+            FTLZW_STACK_PUSH( state->suffix[code-256] );
+            code = state->prefix[code-256];
+          }
+        }
+
+        old_char = code;
+        FTLZW_STACK_PUSH(old_char);
+
+        state->phase = FT_LZW_PHASE_STACK;
+      }
+      /* fall-through */
+
+    case FT_LZW_PHASE_STACK:
+      {
+        while ( state->stack_top > 0 )
+        {
+          --state->stack_top;
+
+          if ( buffer )
+            buffer[result] = state->stack[state->stack_top];
+
+          if ( ++result == out_size )
+            goto Exit;
+        }
+
+        /* now create new entry */
+        if ( free_ent < state->max_free )
+        {
+          if ( free_ent >= state->prefix_size       &&
+               ft_lzwstate_prefix_grow( state ) < 0 )
+            goto Eof;
+
+          FT_ASSERT( free_ent < state->prefix_size );
+
+          state->prefix[free_ent] = (FT_UShort) old_code;
+          state->suffix[free_ent] = (FT_Byte)   old_char;
+
+          if ( ++free_ent == state->free_bits )
+          {
+            num_bits++;
+
+            state->free_bits = num_bits < state->max_bits
+                             ? (FT_UInt)((1UL << num_bits)-256)
+                             : state->max_free+1;
+          }
+        }
+
+        old_code = in_code;
+
+        state->phase = FT_LZW_PHASE_CODE;
+        goto NextCode;
+      }
+
+    default:  /* state == EOF */
+      ;
+  }
+Exit:
+  state->num_bits = num_bits;
+  state->free_ent = free_ent;
+  state->old_code = old_code;
+  state->old_char = old_char;
+  state->in_code  = in_code;
+
+  return  result;
+
+Eof:
+  state->phase = FT_LZW_PHASE_EOF;
+  goto Exit;
+}
--- /dev/null
+++ b/src/lzw/ftzopen.h
@@ -1,0 +1,142 @@
+#ifndef __FT_ZOPEN_H__
+#define __FT_ZOPEN_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+/* this is a complete re-implementation of the LZW file reader,
+ * since the old one was incredibly badly written, and used
+ * 400 Kb of heap memory before decompressing anything.
+ */
+
+#define  FT_LZW_IN_BUFF_SIZE        64
+#define  FT_LZW_DEFAULT_STACK_SIZE  64
+
+#define  LZW_INIT_BITS     9
+#define  LZW_MAX_BITS      16
+
+#define  LZW_CLEAR         256
+#define  LZW_FIRST         257
+
+#define  LZW_BIT_MASK      0x1f
+#define  LZW_BLOCK_MASK    0x80
+#define  LZW_MASK(n)       ((1U << (n)) - 1U)
+
+typedef enum
+{
+  FT_LZW_PHASE_START = 0,
+  FT_LZW_PHASE_CODE,
+  FT_LZW_PHASE_STACK,
+  FT_LZW_PHASE_EOF
+
+} FT_LzwPhase;
+
+
+/* state of LZW decompressor
+ *
+ * small technical note:
+ *
+ * we use a few tricks in this implementation that are explained here to
+ * ease debugging and maintenance.
+ *
+ * - first of all, the "prefix" and "suffix" arrays contain the
+ *   suffix and prefix for codes over 256, this means that:
+ *
+ *     prefix_of(code) == state->prefix[ code-256 ]
+ *     suffix_of(code) == state->suffix[ code-256 ]
+ *
+ *   each prefix is a 16-bit code, and each suffix an 8-bit byte
+ *
+ *   both arrays are stored in a single memory block, pointed to by
+ *   'state->prefix', this means that the following equality is always
+ *   true:
+ *
+ *     state->suffix == (FT_Byte*)(state->prefix + state->prefix_size)
+ *
+ *   of course, state->prefix_size is the number of prefix/suffix slots
+ *   in the arrays, corresponding to codes 256..255+prefix_size
+ *
+ * - 'free_ent' is the index of the next free entry in the "prefix"
+ *   and "suffix" arrays. This means that the corresponding "next free
+ *   code" is really '256+free_ent'
+ *
+ *   moreover, 'max_free' is the maximum value that 'free_ent' can reach.
+ *
+ *   'max_free' corresponds to "(1 << max_bits) - 256". Note that this value
+ *   is always <= 0xFF00, which means that both 'free_ent' and 'max_free' can
+ *   be stored in FT_UInt variable, even on 16-bit machines.
+ *
+ *   if 'free_ent == max_free', you cannot add new codes to the prefix/suffix
+ *   table.
+ *
+ * - 'num_bits' is the current number of code bits, starting at 9 and
+ *   growing each time 'free_ent' reaches the value of 'free_bits'. the
+ *   latter is computed as follows:
+ *
+ *     if num_bits < max_bits:
+ *        free_bits = (1 << num_bits)-256
+ *     else:
+ *        free_bits = max_free + 1
+ *
+ *     since the value of 'max_free + 1' can never be reached by 'free_ent',
+ *     'num_bits' cannot grow larger than 'max_bits'
+ */
+typedef struct
+{
+  FT_LzwPhase  phase;
+
+  FT_Int       in_eof;
+  FT_Byte*     in_cursor;                        /* current buffer pos */
+  FT_Byte*     in_limit;                         /* current buffer limit */
+
+  FT_UInt32    pad;          /* a pad value where incoming bits were read */
+  FT_Int       pad_bits;     /* number of meaningful bits in pad value    */
+
+  FT_UInt      max_bits;     /* max code bits, from file header   */
+  FT_Int       block_mode;   /* block mode flag, from file header */
+  FT_UInt      max_free;     /* (1 << max_bits) - 256             */
+
+  FT_UInt      num_bits;     /* current code bit number */
+  FT_UInt      free_ent;     /* index of next free entry */
+  FT_UInt      free_bits;    /* if free_ent reaches this, increment num_bits */
+  FT_UInt      old_code;
+  FT_UInt      old_char;
+  FT_UInt      in_code;
+
+  FT_UShort*   prefix;       /* always dynamically allocated / reallocated */
+  FT_Byte*     suffix;       /* suffix = (FT_Byte*)(prefix + prefix_size)  */
+  FT_UInt      prefix_size;  /* number of slots in 'prefix' or 'suffix'    */
+
+  FT_Byte*     stack;        /* character stack */
+  FT_UInt      stack_top;
+  FT_UInt      stack_size;
+
+  FT_Byte      in_buff[ FT_LZW_IN_BUFF_SIZE ];   /* small buffer to read data */
+  FT_Byte      stack_0[ FT_LZW_DEFAULT_STACK_SIZE ];  /* minimize heap alloc */
+
+  FT_Stream    source;   /* source stream */
+  FT_Memory    memory;
+
+} FT_LzwStateRec, *FT_LzwState;
+
+
+FT_LOCAL( void )
+ft_lzwstate_init( FT_LzwState  state,
+                  FT_Stream    source );
+
+FT_LOCAL( void )
+ft_lzwstate_done( FT_LzwState  state );
+
+
+FT_LOCAL( void )
+ft_lzwstate_reset( FT_LzwState  state );
+
+
+FT_LOCAL( FT_ULong )
+ft_lzwstate_io( FT_LzwState  state,
+                FT_Byte*     buffer,
+                FT_ULong     out_size );
+
+/* */
+
+#endif /* __FT_ZOPEN_H__ */
--- a/src/lzw/rules.mk
+++ b/src/lzw/rules.mk
@@ -28,12 +28,12 @@
 
 # LZW support sources (i.e., C files)
 #
-LZW_DRV_SRC := $(LZW_DIR)/ftlzw.c \
-               $(LZW_DIR)/zopen.c
+LZW_DRV_SRC := $(LZW_DIR)/ftlzw2.c
 
 # LZW support headers
 #
-LZW_DRV_H := $(LZW_DIR)/zopen.h
+LZW_DRV_H := $(LZW_DIR)/ftzopen.h \
+             $(LZW_DIR)/ftzopen.c
 
 
 # LZW driver object(s)
@@ -41,12 +41,12 @@
 #   LZW_DRV_OBJ_M is used during `multi' builds
 #   LZW_DRV_OBJ_S is used during `single' builds
 #
-LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw.$O
-LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw.$O
+LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw2.$O
+LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw2.$O
 
 # LZW support source file for single build
 #
-LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw.c
+LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw2.c
 
 
 # LZW support - single object