shithub: freetype+ttf2subf

Download patch

ref: 01705395b08167b654a24f26673ee6e75a84f2be
parent: 274207eb9a0e3bb20edf30e9a62e25120d5d15e5
author: Werner Lemberg <[email protected]>
date: Thu Jul 18 09:13:12 EDT 2013

[sfnt] Add support for Apple's `sbix' color bitmap table.

* include/freetype/internal/tttypes.h (TT_SBit_MetricsRec): Widen
fields to FT_Short and FT_UShort, respectively.
(TT_SBitTableType): New enumeration.
(TT_FaceRec): Add `sbit_table_type' field.

* include/freetype/tttags.h (TTAG_sbix): New macro.

* src/sfnt/pngshim.c (Load_SBit_Png): Pass a more generic
FT_GlyphSlot argument instead FT_Bitmap.
Add flag to control map and metrics handling.
Update all users.

* src/sfnt/ttsbit.c: Include `ttmtx.h'.
(tt_face_load_eblc): Renamed to...
(tt_face_load_sbit): This.
Handlic `sbix' bitmaps.
(tt_face_free_eblc): Renamed to...
(tt_face_load_sbit): This.
Updated.
(tt_face_load_strike_metrics): Handle `sbix' bitmaps.
(tt_face_load_sbix_image): New function.
(tt_sbit_decoder_alloc_bitmap, tt_sbit_decoder_load_image,
tt_sbit_decoder_load_byte_aligned, tt_sbit_decoder_load_bit_aligned,
tt_sbit_decoder_load_compound, tt_sbit_decoder_load_png,
tt_sbit_decoder_load_image, tt_sbit_decoder_load_bitmap): Don't pass
and handle load flags.
(tt_sbit_decoder_load_bitmap) [!FT_CONFIG_OPTION_USE_PNG]: Better
handle formats 17-19.
Move color to grayscale conversion to...
(tt_face_load_sbit_image): Here.
Handle `sbix' bitmaps.

* src/sfnt/pngshim.h: Updated.
* src/sfnt/ttsbit.h: Updated.
* src/sfnt/sfdriver.c: Updated.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2013-07-18  Behdad Esfahbod  <[email protected]>
+
+	[sfnt] Add support for Apple's `sbix' color bitmap table.
+
+	* include/freetype/internal/tttypes.h (TT_SBit_MetricsRec): Widen
+	fields to FT_Short and FT_UShort, respectively.
+	(TT_SBitTableType): New enumeration.
+	(TT_FaceRec): Add `sbit_table_type' field.
+
+	* include/freetype/tttags.h (TTAG_sbix): New macro.
+
+	* src/sfnt/pngshim.c (Load_SBit_Png): Pass a more generic
+	FT_GlyphSlot argument instead FT_Bitmap.
+	Add flag to control map and metrics handling.
+	Update all users.
+
+	* src/sfnt/ttsbit.c: Include `ttmtx.h'.
+	(tt_face_load_eblc): Renamed to...
+	(tt_face_load_sbit): This.
+	Handlic `sbix' bitmaps.
+	(tt_face_free_eblc): Renamed to...
+	(tt_face_load_sbit): This.
+	Updated.
+	(tt_face_load_strike_metrics): Handle `sbix' bitmaps.
+	(tt_face_load_sbix_image): New function.
+	(tt_sbit_decoder_alloc_bitmap, tt_sbit_decoder_load_image,
+	tt_sbit_decoder_load_byte_aligned, tt_sbit_decoder_load_bit_aligned,
+	tt_sbit_decoder_load_compound, tt_sbit_decoder_load_png,
+	tt_sbit_decoder_load_image, tt_sbit_decoder_load_bitmap): Don't pass
+	and handle load flags.
+	(tt_sbit_decoder_load_bitmap) [!FT_CONFIG_OPTION_USE_PNG]: Better
+	handle formats 17-19.
+	Move color to grayscale conversion to...
+	(tt_face_load_sbit_image): Here.
+	Handle `sbix' bitmaps.
+
+	* src/sfnt/pngshim.h: Updated.
+	* src/sfnt/ttsbit.h: Updated.
+	* src/sfnt/sfdriver.c: Updated.
+
 2013-07-18  Werner Lemberg  <[email protected]>
 
 	[sfnt] Ignore invalid magic number in `head' or `bhed'.
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -353,16 +353,16 @@
   /*                                                                       */
   typedef struct  TT_SBit_MetricsRec_
   {
-    FT_Byte  height;
-    FT_Byte  width;
+    FT_UShort  height;
+    FT_UShort  width;
 
-    FT_Char  horiBearingX;
-    FT_Char  horiBearingY;
-    FT_Byte  horiAdvance;
+    FT_Short   horiBearingX;
+    FT_Short   horiBearingY;
+    FT_UShort  horiAdvance;
 
-    FT_Char  vertBearingX;
-    FT_Char  vertBearingY;
-    FT_Byte  vertAdvance;
+    FT_Short   vertBearingX;
+    FT_Short   vertBearingY;
+    FT_UShort  vertAdvance;
 
   } TT_SBit_MetricsRec, *TT_SBit_Metrics;
 
@@ -979,6 +979,20 @@
   (*TT_Loader_EndGlyphFunc)( TT_Loader  loader );
 
 
+  typedef enum TT_SbitTableType_
+  {
+    TT_SBIT_TABLE_TYPE_NONE = 0,
+    TT_SBIT_TABLE_TYPE_EBLC, /* `EBLC' (Microsoft), */
+                             /* `bloc' (Apple), or  */
+                             /* `CBLC' (Google)     */
+    TT_SBIT_TABLE_TYPE_SBIX, /* `sbix' (Apple)      */
+
+    /* do not remove */
+    TT_SBIT_TABLE_TYPE_MAX
+
+  } TT_SbitTableType;
+
+
   /*************************************************************************/
   /*                                                                       */
   /*                         TrueType Face Type                            */
@@ -1090,13 +1104,6 @@
   /*                                                                       */
   /*    pclt                 :: The `pclt' SFNT table.                     */
   /*                                                                       */
-  /*    num_sbit_strikes     :: The number of sbit strikes, i.e., bitmap   */
-  /*                            sizes, embedded in this font.              */
-  /*                                                                       */
-  /*    sbit_strikes         :: An array of sbit strikes embedded in this  */
-  /*                            font.  This table is optional in a         */
-  /*                            TrueType/OpenType font.                    */
-  /*                                                                       */
   /*    num_sbit_scales      :: The number of sbit scales for this font.   */
   /*                                                                       */
   /*    sbit_scales          :: Array of sbit scales embedded in this      */
@@ -1302,6 +1309,7 @@
 
     FT_Byte*              sbit_table;
     FT_ULong              sbit_table_size;
+    TT_SbitTableType      sbit_table_type;
     FT_UInt               sbit_num_strikes;
 
     FT_Byte*              kern_table;
--- a/include/freetype/tttags.h
+++ b/include/freetype/tttags.h
@@ -88,6 +88,7 @@
 #define TTAG_post  FT_MAKE_TAG( 'p', 'o', 's', 't' )
 #define TTAG_prep  FT_MAKE_TAG( 'p', 'r', 'e', 'p' )
 #define TTAG_prop  FT_MAKE_TAG( 'p', 'r', 'o', 'p' )
+#define TTAG_sbix  FT_MAKE_TAG( 's', 'b', 'i', 'x' )
 #define TTAG_sfnt  FT_MAKE_TAG( 's', 'f', 'n', 't' )
 #define TTAG_SING  FT_MAKE_TAG( 'S', 'I', 'N', 'G' )
 #define TTAG_trak  FT_MAKE_TAG( 't', 'r', 'a', 'k' )
--- a/src/sfnt/pngshim.c
+++ b/src/sfnt/pngshim.c
@@ -175,7 +175,7 @@
 
 
   static FT_Error
-  Load_SBit_Png( FT_Bitmap*       map,
+  Load_SBit_Png( FT_GlyphSlot     slot,
                  FT_Int           x_offset,
                  FT_Int           y_offset,
                  FT_Int           pix_bits,
@@ -182,8 +182,10 @@
                  TT_SBit_Metrics  metrics,
                  FT_Memory        memory,
                  FT_Byte*         data,
-                 FT_UInt          png_len )
+                 FT_UInt          png_len,
+                 FT_Bool          populate_map_and_metrics )
   {
+    FT_Bitmap    *map   = &slot->bitmap;
     FT_Error      error = FT_Err_Ok;
     FT_StreamRec  stream;
 
@@ -196,14 +198,23 @@
     png_byte*  *rows;
 
 
-    if ( x_offset < 0 || x_offset + metrics->width  > map->width ||
-         y_offset < 0 || y_offset + metrics->height > map->rows  ||
-         pix_bits != 32 || map->pixel_mode != FT_PIXEL_MODE_BGRA )
+    if ( x_offset < 0 ||
+         y_offset < 0 )
     {
       error = FT_THROW( Invalid_Argument );
       goto Exit;
     }
 
+    if ( !populate_map_and_metrics                   &&
+         ( x_offset + metrics->width  > map->width ||
+           y_offset + metrics->height > map->rows  ||
+           pix_bits != 32                          ||
+           map->pixel_mode != FT_PIXEL_MODE_BGRA   ) )
+    {
+      error = FT_THROW( Invalid_Argument );
+      goto Exit;
+    }
+
     FT_Stream_OpenMemory( &stream, data, png_len );
 
     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
@@ -238,10 +249,32 @@
                   &bitdepth, &color_type, &interlace,
                   NULL, NULL );
 
-    if ( error != FT_Err_Ok                   ||
-         (FT_Int)imgWidth  != metrics->width  ||
-         (FT_Int)imgHeight != metrics->height )
+    if ( error                                        ||
+         ( !populate_map_and_metrics                &&
+           ( (FT_Int)imgWidth  != metrics->width  ||
+             (FT_Int)imgHeight != metrics->height ) ) )
       goto DestroyExit;
+
+    if ( populate_map_and_metrics )
+    {
+      FT_Long  size;
+
+
+      metrics->width  = (FT_Int)imgWidth;
+      metrics->height = (FT_Int)imgHeight;
+
+      map->width      = metrics->width;
+      map->rows       = metrics->height;
+      map->pixel_mode = FT_PIXEL_MODE_BGRA;
+      map->pitch      = map->width * 4;
+      map->num_grays  = 256;
+
+      size = map->rows * map->pitch;
+
+      error = ft_glyphslot_alloc_bitmap( slot, size );
+      if ( error )
+        goto DestroyExit;
+    }
 
     /* convert palette/gray image to rgb */
     if ( color_type == PNG_COLOR_TYPE_PALETTE )
--- a/src/sfnt/pngshim.h
+++ b/src/sfnt/pngshim.h
@@ -29,7 +29,7 @@
 #ifdef FT_CONFIG_OPTION_USE_PNG
 
   FT_LOCAL( FT_Error )
-  Load_SBit_Png( FT_Bitmap*       map,
+  Load_SBit_Png( FT_GlyphSlot     slot,
                  FT_Int           x_offset,
                  FT_Int           y_offset,
                  FT_Int           pix_bits,
@@ -36,7 +36,8 @@
                  TT_SBit_Metrics  metrics,
                  FT_Memory        memory,
                  FT_Byte*         data,
-                 FT_UInt          png_len );
+                 FT_UInt          png_len,
+                 FT_Bool          populate_map_and_metrics );
 
 #endif
 
--- a/src/sfnt/sfdriver.c
+++ b/src/sfnt/sfdriver.c
@@ -499,8 +499,8 @@
     tt_face_load_hmtx,
 
     /* see `ttsbit.h' and `sfnt.h' */
-    PUT_EMBEDDED_BITMAPS( tt_face_load_eblc ),
-    PUT_EMBEDDED_BITMAPS( tt_face_free_eblc ),
+    PUT_EMBEDDED_BITMAPS( tt_face_load_sbit ),
+    PUT_EMBEDDED_BITMAPS( tt_face_free_sbit ),
 
     PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike     ),
     PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ),
--- a/src/sfnt/ttsbit.c
+++ b/src/sfnt/ttsbit.c
@@ -28,6 +28,7 @@
 
 #include "sferrors.h"
 
+#include "ttmtx.h"
 #include "pngshim.h"
 
 
@@ -42,17 +43,16 @@
 
 
   FT_LOCAL_DEF( FT_Error )
-  tt_face_load_eblc( TT_Face    face,
+  tt_face_load_sbit( TT_Face    face,
                      FT_Stream  stream )
   {
     FT_Error  error = FT_Err_Ok;
-    FT_Fixed  version;
-    FT_ULong  num_strikes, table_size;
-    FT_Byte*  p;
-    FT_Byte*  p_limit;
-    FT_UInt   count;
+    FT_ULong  table_size;
 
 
+    face->sbit_table       = NULL;
+    face->sbit_table_size  = 0;
+    face->sbit_table_type  = TT_SBIT_TABLE_TYPE_NONE;
     face->sbit_num_strikes = 0;
 
     /* this table is optional */
@@ -61,7 +61,16 @@
       error = face->goto_table( face, TTAG_EBLC, stream, &table_size );
     if ( error )
       error = face->goto_table( face, TTAG_bloc, stream, &table_size );
+    if ( !error )
+      face->sbit_table_type = TT_SBIT_TABLE_TYPE_EBLC;
+
     if ( error )
+    {
+      error = face->goto_table( face, TTAG_sbix, stream, &table_size );
+      if ( !error )
+        face->sbit_table_type = TT_SBIT_TABLE_TYPE_SBIX;
+    }
+    if ( error )
       goto Exit;
 
     if ( table_size < 8 )
@@ -71,47 +80,122 @@
       goto Exit;
     }
 
-    if ( FT_FRAME_EXTRACT( table_size, face->sbit_table ) )
-      goto Exit;
+    switch ( (FT_UInt)face->sbit_table_type )
+    {
+    case TT_SBIT_TABLE_TYPE_EBLC:
+      {
+        FT_Byte*  p;
+        FT_Fixed  version;
+        FT_ULong  num_strikes;
+        FT_UInt   count;
 
-    face->sbit_table_size = table_size;
 
-    p       = face->sbit_table;
-    p_limit = p + table_size;
+        if ( FT_FRAME_EXTRACT( table_size, face->sbit_table ) )
+          goto Exit;
 
-    version     = FT_NEXT_ULONG( p );
-    num_strikes = FT_NEXT_ULONG( p );
+        face->sbit_table_size = table_size;
 
-    if ( version != 0x00020000UL || num_strikes >= 0x10000UL )
-    {
-      FT_ERROR(( "tt_face_load_sbit_strikes: invalid table version\n" ));
-      error = FT_THROW( Invalid_File_Format );
-      goto Fail;
+        p = face->sbit_table;
+
+        version     = FT_NEXT_ULONG( p );
+        num_strikes = FT_NEXT_ULONG( p );
+
+        if ( ( version & 0xFFFF0000UL ) != 0x00020000UL )
+        {
+          error = FT_THROW( Unknown_File_Format );
+          goto Exit;
+        }
+
+        if ( num_strikes >= 0x10000UL )
+        {
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+
+        /*
+         *  Count the number of strikes available in the table.  We are a bit
+         *  paranoid there and don't trust the data.
+         */
+        count = (FT_UInt)num_strikes;
+        if ( 8 + 48UL * count > table_size )
+          count = (FT_UInt)( ( table_size - 8 ) / 48 );
+
+        face->sbit_num_strikes = count;
+      }
+      break;
+
+    case TT_SBIT_TABLE_TYPE_SBIX:
+      {
+        FT_UShort  version;
+        FT_UShort  flags;
+        FT_ULong   num_strikes;
+        FT_UInt    count;
+
+
+        if ( FT_FRAME_ENTER( 8 ) )
+          goto Exit;
+
+        version     = FT_GET_USHORT();
+        flags       = FT_GET_USHORT();
+        num_strikes = FT_GET_ULONG();
+
+        FT_FRAME_EXIT();
+
+        if ( version != 1 )
+        {
+          error = FT_THROW( Unknown_File_Format );
+          goto Exit;
+        }
+        if ( flags != 0x0001 || num_strikes >= 0x10000UL )
+        {
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+
+        /*
+         *  Count the number of strikes available in the table.  We are a bit
+         *  paranoid there and don't trust the data.
+         */
+        count = (FT_UInt)num_strikes;
+        if ( 8 + 4UL * count > table_size )
+          count = (FT_UInt)( ( table_size - 8 ) / 4 );
+
+        if ( FT_STREAM_SEEK( FT_STREAM_POS() - 8 ) )
+          goto Exit;
+
+        face->sbit_table_size = 8 + count * 4;
+        if ( FT_FRAME_EXTRACT( face->sbit_table_size, face->sbit_table ) )
+          goto Exit;
+
+        face->sbit_num_strikes = count;
+      }
+      break;
+
+    default:
+      error = FT_THROW( Unknown_File_Format );
+      break;
     }
 
-    /*
-     *  Count the number of strikes available in the table.  We are a bit
-     *  paranoid there and don't trust the data.
-     */
-    count = (FT_UInt)num_strikes;
-    if ( 8 + 48UL * count > table_size )
-      count = (FT_UInt)( ( p_limit - p ) / 48 );
+    if ( !error )
+      FT_TRACE3(( "sbit_num_strikes: %u\n", face->sbit_num_strikes ));
 
-    face->sbit_num_strikes = count;
+    return FT_Err_Ok;
 
-    FT_TRACE3(( "sbit_num_strikes: %u\n", count ));
   Exit:
-    return error;
+    if ( error )
+    {
+      if ( face->sbit_table )
+        FT_FRAME_RELEASE( face->sbit_table );
+      face->sbit_table_size = 0;
+      face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE;
+    }
 
-  Fail:
-    FT_FRAME_RELEASE( face->sbit_table );
-    face->sbit_table_size = 0;
-    goto Exit;
+    return error;
   }
 
 
   FT_LOCAL_DEF( void )
-  tt_face_free_eblc( TT_Face  face )
+  tt_face_free_sbit( TT_Face  face )
   {
     FT_Stream  stream = face->root.stream;
 
@@ -118,6 +202,7 @@
 
     FT_FRAME_RELEASE( face->sbit_table );
     face->sbit_table_size  = 0;
+    face->sbit_table_type  = TT_SBIT_TABLE_TYPE_NONE;
     face->sbit_num_strikes = 0;
   }
 
@@ -136,28 +221,84 @@
                                FT_ULong          strike_index,
                                FT_Size_Metrics*  metrics )
   {
-    FT_Byte*  strike;
-
-
     if ( strike_index >= (FT_ULong)face->sbit_num_strikes )
       return FT_THROW( Invalid_Argument );
 
-    strike = face->sbit_table + 8 + strike_index * 48;
+    switch ( (FT_UInt)face->sbit_table_type )
+    {
+    case TT_SBIT_TABLE_TYPE_EBLC:
+      {
+        FT_Byte*  strike;
 
-    metrics->x_ppem = (FT_UShort)strike[44];
-    metrics->y_ppem = (FT_UShort)strike[45];
 
-    metrics->ascender  = (FT_Char)strike[16] << 6;  /* hori.ascender  */
-    metrics->descender = (FT_Char)strike[17] << 6;  /* hori.descender */
-    metrics->height    = metrics->ascender - metrics->descender;
+        strike = face->sbit_table + 8 + strike_index * 48;
 
-    /* XXX: Is this correct? */
-    metrics->max_advance = ( (FT_Char)strike[22] + /* min_origin_SB  */
-                                      strike[18] + /* max_width      */
-                             (FT_Char)strike[23]   /* min_advance_SB */
-                                                 ) << 6;
+        metrics->x_ppem = (FT_UShort)strike[44];
+        metrics->y_ppem = (FT_UShort)strike[45];
 
-    return FT_Err_Ok;
+        metrics->ascender  = (FT_Char)strike[16] << 6;  /* hori.ascender  */
+        metrics->descender = (FT_Char)strike[17] << 6;  /* hori.descender */
+        metrics->height    = metrics->ascender - metrics->descender;
+
+        /* Is this correct? */
+        metrics->max_advance = ( (FT_Char)strike[22] + /* min_origin_SB  */
+                                          strike[18] + /* max_width      */
+                                 (FT_Char)strike[23]   /* min_advance_SB */
+                                                     ) << 6;
+        return FT_Err_Ok;
+      }
+
+    case TT_SBIT_TABLE_TYPE_SBIX:
+      {
+        FT_Stream       stream = face->root.stream;
+        FT_UInt         offset, ppem, resolution, upem;
+        TT_HoriHeader  *hori;
+        FT_ULong        table_size;
+
+        FT_Error  error = FT_Err_Ok;
+        FT_Byte*  p;
+
+
+        p      = face->sbit_table + 8 + 4 * strike_index;
+        offset = FT_NEXT_ULONG( p );
+
+        error = face->goto_table( face, TTAG_sbix, stream, &table_size );
+        if ( error )
+          return error;
+
+        if ( offset + 4  > table_size )
+          return FT_THROW( Invalid_File_Format );
+
+        if ( FT_STREAM_SEEK( FT_STREAM_POS() + offset ) ||
+             FT_FRAME_ENTER( 4 )                        )
+          return error;
+
+        ppem       = FT_GET_USHORT();
+        resolution = FT_GET_USHORT();
+
+        FT_UNUSED( resolution ); /* What to do with this? */
+
+        FT_FRAME_EXIT();
+
+        upem = face->header.Units_Per_EM;
+        hori = &face->horizontal;
+
+        metrics->x_ppem = ppem;
+        metrics->y_ppem = ppem;
+
+        metrics->ascender    = ppem * hori->Ascender / upem;
+        metrics->descender   = ppem * hori->Descender / upem;
+        metrics->height      = ppem * ( hori->Ascender -
+                                        hori->Descender +
+                                        hori->Line_Gap ) / upem;
+        metrics->max_advance = ppem * hori->advance_Width_Max / upem;
+
+        return error;
+      }
+
+    default:
+      return FT_THROW( Unknown_File_Format );
+    }
   }
 
 
@@ -253,8 +394,7 @@
 
 
   static FT_Error
-  tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder  decoder,
-                                FT_UInt         load_flags )
+  tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder  decoder )
   {
     FT_Error    error = FT_Err_Ok;
     FT_UInt     width, height;
@@ -301,18 +441,9 @@
       break;
 
     case 32:
-      if ( load_flags & FT_LOAD_COLOR )
-      {
-        map->pixel_mode = FT_PIXEL_MODE_BGRA;
-        map->pitch      = map->width * 4;
-        map->num_grays  = 256;
-      }
-      else
-      {
-        map->pixel_mode = FT_PIXEL_MODE_GRAY;
-        map->pitch      = map->width;
-        map->num_grays  = 256;
-      }
+      map->pixel_mode = FT_PIXEL_MODE_BGRA;
+      map->pitch      = map->width * 4;
+      map->num_grays  = 256;
       break;
 
     default:
@@ -382,13 +513,11 @@
   /* forward declaration */
   static FT_Error
   tt_sbit_decoder_load_image( TT_SBitDecoder  decoder,
-                              FT_UInt         load_flags,
                               FT_UInt         glyph_index,
                               FT_Int          x_pos,
                               FT_Int          y_pos );
 
   typedef FT_Error  (*TT_SBitDecoder_LoadFunc)( TT_SBitDecoder  decoder,
-                                                FT_UInt         load_flags,
                                                 FT_Byte*        p,
                                                 FT_Byte*        plimit,
                                                 FT_Int          x_pos,
@@ -397,7 +526,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_byte_aligned( TT_SBitDecoder  decoder,
-                                     FT_UInt         load_flags,
                                      FT_Byte*        p,
                                      FT_Byte*        limit,
                                      FT_Int          x_pos,
@@ -408,9 +536,7 @@
     FT_Int      bit_height, bit_width, pitch, width, height, line_bits, h;
     FT_Bitmap*  bitmap;
 
-    FT_UNUSED( load_flags );
 
-
     /* check that we can write the glyph into the bitmap */
     bitmap     = decoder->bitmap;
     bit_width  = bitmap->width;
@@ -538,7 +664,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_bit_aligned( TT_SBitDecoder  decoder,
-                                    FT_UInt         load_flags,
                                     FT_Byte*        p,
                                     FT_Byte*        limit,
                                     FT_Int          x_pos,
@@ -550,9 +675,7 @@
     FT_Bitmap*  bitmap;
     FT_UShort   rval;
 
-    FT_UNUSED( load_flags );
 
-
     /* check that we can write the glyph into the bitmap */
     bitmap     = decoder->bitmap;
     bit_width  = bitmap->width;
@@ -664,7 +787,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_compound( TT_SBitDecoder  decoder,
-                                 FT_UInt         load_flags,
                                  FT_Byte*        p,
                                  FT_Byte*        limit,
                                  FT_Int          x_pos,
@@ -702,7 +824,7 @@
 
 
       /* NB: a recursive call */
-      error = tt_sbit_decoder_load_image( decoder, load_flags, gindex,
+      error = tt_sbit_decoder_load_image( decoder, gindex,
                                           x_pos + dx, y_pos + dy );
       if ( error )
         break;
@@ -732,7 +854,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_png( TT_SBitDecoder  decoder,
-                            FT_UInt         load_flags,
                             FT_Byte*        p,
                             FT_Byte*        limit,
                             FT_Int          x_pos,
@@ -741,9 +862,7 @@
     FT_Error  error = FT_Err_Ok;
     FT_ULong  png_len;
 
-    FT_UNUSED( load_flags );
 
-
     if ( limit - p < 4 )
     {
       FT_TRACE1(( "tt_sbit_decoder_load_png: broken bitmap\n" ));
@@ -759,7 +878,7 @@
       goto Exit;
     }
 
-    error = Load_SBit_Png( decoder->bitmap,
+    error = Load_SBit_Png( decoder->face->root.glyph,
                            x_pos,
                            y_pos,
                            decoder->bit_depth,
@@ -766,7 +885,8 @@
                            decoder->metrics,
                            decoder->stream->memory,
                            p,
-                           png_len );
+                           png_len,
+                           FALSE );
 
   Exit:
     if ( !error )
@@ -779,7 +899,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_bitmap( TT_SBitDecoder  decoder,
-                               FT_UInt         load_flags,
                                FT_UInt         glyph_format,
                                FT_ULong        glyph_start,
                                FT_ULong        glyph_size,
@@ -859,13 +978,15 @@
         loader = tt_sbit_decoder_load_compound;
         break;
 
-#ifdef FT_CONFIG_OPTION_USE_PNG
       case 17: /* small metrics, PNG image data   */
       case 18: /* big metrics, PNG image data     */
       case 19: /* metrics in EBLC, PNG image data */
+#ifdef FT_CONFIG_OPTION_USE_PNG
         loader = tt_sbit_decoder_load_png;
-        break;
+#else
+        error = FT_THROW( Unimplemented_Feature );
 #endif /* FT_CONFIG_OPTION_USE_PNG */
+        break;
 
       default:
         error = FT_THROW( Invalid_Table );
@@ -874,64 +995,12 @@
 
       if ( !decoder->bitmap_allocated )
       {
-        error = tt_sbit_decoder_alloc_bitmap( decoder, load_flags );
+        error = tt_sbit_decoder_alloc_bitmap( decoder );
         if ( error )
           goto Fail;
       }
 
-      if ( decoder->bit_depth == 32                          &&
-           decoder->bitmap->pixel_mode != FT_PIXEL_MODE_BGRA )
-      {
-        /* Flatten color bitmaps if color was not requested. */
-
-        FT_Library library = decoder->face->root.glyph->library;
-        FT_Memory  memory  = decoder->stream->memory;
-
-        FT_Bitmap color, *orig;
-
-
-        if ( decoder->bitmap->pixel_mode != FT_PIXEL_MODE_GRAY ||
-             x_pos != 0 || y_pos != 0                          )
-        {
-          /* Shouldn't happen. */
-          error = FT_THROW( Invalid_Table );
-          goto Fail;
-        }
-
-        FT_Bitmap_New( &color );
-
-        color.rows       = decoder->bitmap->rows;
-        color.width      = decoder->bitmap->width;
-        color.pitch      = color.width * 4;
-        color.pixel_mode = FT_PIXEL_MODE_BGRA;
-
-        if ( FT_ALLOC( color.buffer, color.rows * color.pitch ) )
-          goto Fail;
-
-        orig            = decoder->bitmap;
-        decoder->bitmap = &color;
-
-        error = loader( decoder, load_flags, p, p_limit, x_pos, y_pos );
-
-        decoder->bitmap = orig;
-
-        /* explicitly test against FT_Err_Ok to avoid compiler warnings */
-        /* (we do an assignment within a conditional)                   */
-        if ( error                                           ||
-             ( error = FT_Bitmap_Convert( library,
-                                          &color,
-                                          decoder->bitmap,
-                                          1 ) ) != FT_Err_Ok )
-        {
-          FT_Bitmap_Done( library, &color );
-          goto Fail;
-        }
-
-        FT_Bitmap_Done( library, &color );
-      }
-
-      else
-        error = loader( decoder, load_flags, p, p_limit, x_pos, y_pos );
+      error = loader( decoder, p, p_limit, x_pos, y_pos );
     }
 
   Fail:
@@ -944,7 +1013,6 @@
 
   static FT_Error
   tt_sbit_decoder_load_image( TT_SBitDecoder  decoder,
-                              FT_UInt         load_flags,
                               FT_UInt         glyph_index,
                               FT_Int          x_pos,
                               FT_Int          y_pos )
@@ -993,17 +1061,15 @@
     switch ( index_format )
     {
     case 1: /* 4-byte offsets relative to `image_offset' */
-      {
-        p += 4 * ( glyph_index - start );
-        if ( p + 8 > p_limit )
-          goto NoBitmap;
+      p += 4 * ( glyph_index - start );
+      if ( p + 8 > p_limit )
+        goto NoBitmap;
 
-        image_start = FT_NEXT_ULONG( p );
-        image_end   = FT_NEXT_ULONG( p );
+      image_start = FT_NEXT_ULONG( p );
+      image_end   = FT_NEXT_ULONG( p );
 
-        if ( image_start == image_end )  /* missing glyph */
-          goto NoBitmap;
-      }
+      if ( image_start == image_end )  /* missing glyph */
+        goto NoBitmap;
       break;
 
     case 2: /* big metrics, constant image size */
@@ -1025,17 +1091,15 @@
       break;
 
     case 3: /* 2-byte offsets relative to 'image_offset' */
-      {
-        p += 2 * ( glyph_index - start );
-        if ( p + 4 > p_limit )
-          goto NoBitmap;
+      p += 2 * ( glyph_index - start );
+      if ( p + 4 > p_limit )
+        goto NoBitmap;
 
-        image_start = FT_NEXT_USHORT( p );
-        image_end   = FT_NEXT_USHORT( p );
+      image_start = FT_NEXT_USHORT( p );
+      image_end   = FT_NEXT_USHORT( p );
 
-        if ( image_start == image_end )  /* missing glyph */
-          goto NoBitmap;
-      }
+      if ( image_start == image_end )  /* missing glyph */
+        goto NoBitmap;
       break;
 
     case 4: /* sparse glyph array with (glyph,offset) pairs */
@@ -1124,7 +1188,6 @@
                 image_format, glyph_index ));
 
     return tt_sbit_decoder_load_bitmap( decoder,
-                                        load_flags,
                                         image_format,
                                         image_start,
                                         image_end,
@@ -1142,6 +1205,129 @@
   }
 
 
+  static FT_Error
+  tt_face_load_sbix_image( TT_Face              face,
+                           FT_ULong             strike_index,
+                           FT_UInt              glyph_index,
+                           FT_Stream            stream,
+                           FT_Bitmap           *map,
+                           TT_SBit_MetricsRec  *metrics )
+  {
+    FT_UInt     sbix_pos, strike_offset, glyph_start, glyph_end;
+    FT_ULong    table_size, data_size;
+    FT_Int      originOffsetX, originOffsetY;
+    FT_Tag      graphicType;
+    FT_Int      recurse_depth = 0;
+
+    FT_Error  error = FT_Err_Ok;
+    FT_Byte*  p;
+
+    FT_UNUSED( map );
+
+
+    metrics->width  = 0;
+    metrics->height = 0;
+
+    p = face->sbit_table + 8 + 4 * strike_index;
+    strike_offset = FT_NEXT_ULONG( p );
+
+    error = face->goto_table( face, TTAG_sbix, stream, &table_size );
+    if ( error )
+      return error;
+    sbix_pos = FT_STREAM_POS();
+
+  retry:
+    if ( glyph_index > (FT_UInt)face->root.num_glyphs )
+      return FT_THROW( Invalid_Argument );
+
+    if ( strike_offset >= table_size                          ||
+         table_size - strike_offset < 4 + glyph_index * 4 + 8 )
+      return FT_THROW( Invalid_File_Format );
+
+    if ( FT_STREAM_SEEK( sbix_pos + strike_offset + 4 + glyph_index * 4 ) ||
+         FT_FRAME_ENTER( 8 )                                              )
+      return error;
+
+    glyph_start = FT_GET_ULONG();
+    glyph_end   = FT_GET_ULONG();
+
+    FT_FRAME_EXIT();
+
+    if ( glyph_start == glyph_end )
+      return FT_THROW( Invalid_Argument );
+    if ( glyph_start > glyph_end                ||
+         glyph_end - glyph_start < 8            ||
+         table_size - strike_offset < glyph_end )
+      return FT_THROW( Invalid_File_Format );
+
+    if ( FT_STREAM_SEEK( sbix_pos + strike_offset + glyph_start ) ||
+         FT_FRAME_ENTER( glyph_end - glyph_start )                )
+      return error;
+
+    originOffsetX = FT_GET_SHORT();
+    originOffsetY = FT_GET_SHORT();
+
+    graphicType = FT_GET_TAG4();
+    data_size   = glyph_end - glyph_start - 8;
+
+    switch ( graphicType )
+    {
+    case FT_MAKE_TAG( 'd', 'u', 'p', 'e' ):
+      if ( recurse_depth < 4 )
+      {
+        glyph_index = FT_GET_USHORT();
+        FT_FRAME_EXIT();
+        recurse_depth++;
+        goto retry;
+      }
+      error = FT_THROW( Invalid_File_Format );
+      break;
+
+    case FT_MAKE_TAG( 'p', 'n', 'g', ' ' ):
+#ifdef FT_CONFIG_OPTION_USE_PNG
+      error = Load_SBit_Png( face->root.glyph,
+                             0,
+                             0,
+                             32,
+                             metrics,
+                             stream->memory,
+                             stream->cursor,
+                             data_size,
+                             TRUE );
+#else
+      error = FT_THROW( Unimplemented_Feature );
+#endif
+      break;
+
+    case FT_MAKE_TAG( 'j', 'p', 'g', ' ' ):
+    case FT_MAKE_TAG( 't', 'i', 'f', 'f' ):
+      error = FT_THROW( Unknown_File_Format );
+      break;
+
+    default:
+      error = FT_THROW( Unimplemented_Feature );
+      break;
+    }
+
+    FT_FRAME_EXIT();
+
+    if ( !error )
+    {
+      FT_Short   abearing;
+      FT_UShort  aadvance;
+
+
+      tt_face_get_metrics( face, FALSE, glyph_index, &abearing, &aadvance );
+
+      metrics->horiBearingX = originOffsetX;
+      metrics->horiBearingY = -originOffsetY + metrics->height;
+      metrics->horiAdvance  = aadvance * face->root.size->metrics.x_ppem /
+                                face->root.units_per_EM;
+    }
+
+    return error;
+  }
+
   FT_LOCAL( FT_Error )
   tt_face_load_sbit_image( TT_Face              face,
                            FT_ULong             strike_index,
@@ -1151,23 +1337,66 @@
                            FT_Bitmap           *map,
                            TT_SBit_MetricsRec  *metrics )
   {
-    TT_SBitDecoderRec  decoder[1];
-    FT_Error           error;
+    FT_Error  error = FT_Err_Ok;
 
-    FT_UNUSED( load_flags );
-    FT_UNUSED( stream );
-    FT_UNUSED( map );
 
+    switch ( (FT_UInt)face->sbit_table_type )
+    {
+    case TT_SBIT_TABLE_TYPE_EBLC:
+      {
+        TT_SBitDecoderRec  decoder[1];
 
-    error = tt_sbit_decoder_init( decoder, face, strike_index, metrics );
-    if ( !error )
+
+        error = tt_sbit_decoder_init( decoder, face, strike_index, metrics );
+        if ( !error )
+        {
+          error = tt_sbit_decoder_load_image( decoder,
+                                              glyph_index,
+                                              0,
+                                              0 );
+          tt_sbit_decoder_done( decoder );
+        }
+      }
+      break;
+
+    case TT_SBIT_TABLE_TYPE_SBIX:
+      error = tt_face_load_sbix_image( face,
+                                       strike_index,
+                                       glyph_index,
+                                       stream,
+                                       map,
+                                       metrics );
+      break;
+
+    default:
+      error = FT_THROW( Unknown_File_Format );
+      break;
+    }
+
+    /* Flatten color bitmaps if color was not requested. */
+    if ( !error                                &&
+         !( load_flags & FT_LOAD_COLOR )       &&
+         map->pixel_mode == FT_PIXEL_MODE_BGRA )
     {
-      error = tt_sbit_decoder_load_image( decoder,
-                                          load_flags,
-                                          glyph_index,
-                                          0,
-                                          0 );
-      tt_sbit_decoder_done( decoder );
+      FT_Bitmap   new_map;
+      FT_Library  library = face->root.glyph->library;
+
+
+      FT_Bitmap_New( &new_map );
+
+      /* Convert to 8bit grayscale. */
+      error = FT_Bitmap_Convert( library, map, &new_map, 1 );
+      if ( error )
+        FT_Bitmap_Done( library, &new_map );
+      else
+      {
+        map->pixel_mode = new_map.pixel_mode;
+        map->pitch      = new_map.pitch;
+        map->num_grays  = new_map.num_grays;
+
+        ft_glyphslot_set_bitmap( face->root.glyph, new_map.buffer );
+        face->root.glyph->internal->flags |= FT_GLYPH_OWN_BITMAP;
+      }
     }
 
     return error;
--- a/src/sfnt/ttsbit.h
+++ b/src/sfnt/ttsbit.h
@@ -28,11 +28,11 @@
 
 
   FT_LOCAL( FT_Error )
-  tt_face_load_eblc( TT_Face    face,
+  tt_face_load_sbit( TT_Face    face,
                      FT_Stream  stream );
 
   FT_LOCAL( void )
-  tt_face_free_eblc( TT_Face  face );
+  tt_face_free_sbit( TT_Face  face );
 
 
   FT_LOCAL( FT_Error )