shithub: freetype+ttf2subf

Download patch

ref: f04d81751ac68b23c89da2edd31c43fd9fc5c75c
parent: 84eebf48021ab76c1c628bb7451b712f701e4518
author: Shao Yu Zhang <[email protected]>
date: Sat May 12 23:25:09 EDT 2018

[sfnt] Preliminary support of coloured layer outlines.

This commit enables OpenType's COLR/CPAL table handling; a typical
application are color emojis that can be scaled to any size.

If the color palette does not exist or is invalid, the rendering
step rasterizes the outline instead.  The current implementation
assumes that the foreground is black.

Enable this by defining option TT_CONFIG_OPTION_COLOR_LAYERS.

There are still some issues with metrics; additionally, an API to
fetch color layers is missing.

* devel/ftoption.h, include/freetype/config/ftoption.h
(TT_CONFIG_OPTION_COLOR_LAYERS): New macro.

* include/freetype/internal/ftobjs.h (FT_Glyph_LayerRec,
FT_Colr_InternalRec): New structures.
(FT_Slot_InternalRec): Add `color_layers' field.

* include/freetype/internal/sfnt.h (TT_Load_Colr_Layer_Func,
TT_Blend_Colr_Func): New function types.
(SFNT_Interface): Add `load_colr', `free_colr', `load_colr_layer',
and `colr_blend' fields.

* include/freetype/internal/tttypes.h (TT_FaceRec): Add
`colr_and_cpal' field.

* include/freetype/internal/tttags. (TTAG_COLR, TTAG_CPAL): New
macros.

* src/sfnt/ttcolr.c, src/sfnt/ttcolr.h: New files.

* src/base/ftobjs.c (ft_glyphslot_done, FT_Render_Glyph_Internal):
Handle glyph color layers.

* src/sfnt/Jamfile (_sources), src/sfnt/rules.mk (SFNT_DRV_SRC): Add
`ttcolr.c'.

* src/sfnt/sfdriver.c: Include `ttcolr.h'.
(PUT_COLOR_LAYERS): New macro.
Update call to `FT_DEFINE_SFNT_INTERFACE'.

* src/sfnt/sfnt.c: Include `ttcolr.c'.

* src/sfnt/sfobjs.c (sfnt_load_face): Load `COLR' and `CPAL' tables.
(sfnt_done_face): Updated.

* src/truetype/ttgload.c (TT_Load_Glyph): Handle color layers.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,57 @@
+2018-05-13  Shao Yu Zhang  <[email protected]>
+	    Werner Lemberg  <[email protected]>
+
+	[sfnt] Preliminary support of coloured layer outlines.
+
+	This commit enables OpenType's COLR/CPAL table handling; a typical
+	application are color emojis that can be scaled to any size.
+
+	If the color palette does not exist or is invalid, the rendering
+	step rasterizes the outline instead.  The current implementation
+	assumes that the foreground is black.
+
+	Enable this by defining option TT_CONFIG_OPTION_COLOR_LAYERS.
+
+	There are still some issues with metrics; additionally, an API to
+	fetch color layers is missing.
+
+	* devel/ftoption.h, include/freetype/config/ftoption.h
+	(TT_CONFIG_OPTION_COLOR_LAYERS): New macro.
+
+	* include/freetype/internal/ftobjs.h (FT_Glyph_LayerRec,
+	FT_Colr_InternalRec): New structures.
+	(FT_Slot_InternalRec): Add `color_layers' field.
+
+	* include/freetype/internal/sfnt.h (TT_Load_Colr_Layer_Func,
+	TT_Blend_Colr_Func): New function types.
+	(SFNT_Interface): Add `load_colr', `free_colr', `load_colr_layer',
+	and `colr_blend' fields.
+
+	* include/freetype/internal/tttypes.h (TT_FaceRec): Add
+	`colr_and_cpal' field.
+
+	* include/freetype/internal/tttags. (TTAG_COLR, TTAG_CPAL): New
+	macros.
+
+	* src/sfnt/ttcolr.c, src/sfnt/ttcolr.h: New files.
+
+	* src/base/ftobjs.c (ft_glyphslot_done, FT_Render_Glyph_Internal):
+	Handle glyph color layers.
+
+	* src/sfnt/Jamfile (_sources), src/sfnt/rules.mk (SFNT_DRV_SRC): Add
+	`ttcolr.c'.
+
+	* src/sfnt/sfdriver.c: Include `ttcolr.h'.
+	(PUT_COLOR_LAYERS): New macro.
+	Update call to `FT_DEFINE_SFNT_INTERFACE'.
+
+	* src/sfnt/sfnt.c: Include `ttcolr.c'.
+
+	* src/sfnt/sfobjs.c (sfnt_load_face): Load `COLR' and `CPAL' tables.
+	(sfnt_done_face): Updated.
+
+	* src/truetype/ttgload.c (TT_Load_Glyph): Handle color layers.
+
 2018-05-12  Arkady Shapkin  <[email protected]>
 
 	Use MS VC++'s _BitScanReverse to calculate MSB (patch #9636).
--- a/devel/ftoption.h
+++ b/devel/ftoption.h
@@ -487,6 +487,15 @@
 
   /*************************************************************************/
   /*                                                                       */
+  /* Define TT_CONFIG_OPTION_COLOR_LAYERS if you want to support coloured  */
+  /* outlines (from the COLR/CPAL tables) in all formats using the SFNT    */
+  /* module (namely TrueType & OpenType).                                  */
+  /*                                                                       */
+#define TT_CONFIG_OPTION_COLOR_LAYERS
+
+
+  /*************************************************************************/
+  /*                                                                       */
   /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to    */
   /* load and enumerate the glyph Postscript names in a TrueType or        */
   /* OpenType file.                                                        */
--- a/include/freetype/config/ftoption.h
+++ b/include/freetype/config/ftoption.h
@@ -506,6 +506,15 @@
 
   /*************************************************************************/
   /*                                                                       */
+  /* Define TT_CONFIG_OPTION_COLOR_LAYERS if you want to support coloured  */
+  /* outlines (from the COLR/CPAL tables) in all formats using the SFNT    */
+  /* module (namely TrueType & OpenType).                                  */
+  /*                                                                       */
+#define TT_CONFIG_OPTION_COLOR_LAYERS
+
+
+  /*************************************************************************/
+  /*                                                                       */
   /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to    */
   /* load and enumerate the glyph Postscript names in a TrueType or        */
   /* OpenType file.                                                        */
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -384,6 +384,23 @@
   } FT_Face_InternalRec;
 
 
+  typedef struct  FT_Glyph_LayerRec_
+  {
+    FT_UShort  glyph_index;
+    FT_UShort  color_index;
+
+  } FT_Glyph_LayerRec;
+
+
+  typedef struct  FT_Colr_InternalRec_
+  {
+    FT_Glyph_LayerRec*  layers;
+    FT_UShort           num_layers;
+    FT_Int              load_flags;
+
+  } FT_Colr_InternalRec, *FT_Colr_Internal;
+
+
   /*************************************************************************/
   /*                                                                       */
   /* <Struct>                                                              */
@@ -417,6 +434,8 @@
   /*                                                                       */
   /*    glyph_hints       :: Format-specific glyph hints management.       */
   /*                                                                       */
+  /*    color_layers      :: Data from (SFNT) COLR/CPAL tables.            */
+  /*                                                                       */
 
 #define FT_GLYPH_OWN_BITMAP  0x1U
 
@@ -428,6 +447,8 @@
     FT_Matrix       glyph_matrix;
     FT_Vector       glyph_delta;
     void*           glyph_hints;
+
+    FT_Colr_Internal  color_layers;
 
   } FT_GlyphSlot_InternalRec;
 
--- a/include/freetype/internal/sfnt.h
+++ b/include/freetype/internal/sfnt.h
@@ -431,6 +431,68 @@
   /*************************************************************************/
   /*                                                                       */
   /* <FuncType>                                                            */
+  /*    TT_Load_Colr_Layer_Func                                            */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Load the color layer data given a glyph index.                     */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    face       :: The target face object.                              */
+  /*                                                                       */
+  /*    idx        :: The glyph index.                                     */
+  /*                                                                       */
+  /* <Output>                                                              */
+  /*    layers     :: The layer info with color index and glyph index.     */
+  /*                  Deallocate with `FT_FREE'.                           */
+  /*                                                                       */
+  /*    num_layers :: Number of layers.                                    */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    FreeType error code.  0 means success.  Returns an error if no     */
+  /*    color layer information exists for `idx'.                          */
+  /*                                                                       */
+  typedef FT_Error
+  (*TT_Load_Colr_Layer_Func)( TT_Face              face,
+                              FT_Int               idx,
+                              FT_Glyph_LayerRec*  *layers,
+                              FT_UShort*           num_layers );
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    TT_Blend_Colr_Func                                                 */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Blend the bitmap in `new_glyph' into `base_glyph' using the color  */
+  /*    specified by `color_index'.                                        */
+  /*                                                                       */
+  /*    XXX: Handle foregound color                                        */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    face        :: The target face object.                             */
+  /*                                                                       */
+  /*    color_index :: Color index from the COLR table.                    */
+  /*                                                                       */
+  /*    base_glyph  :: Slot for bitmap to be merged into.  The underlying  */
+  /*                   bitmap may get reallocated.                         */
+  /*                                                                       */
+  /*    new_glyph   :: Slot to be incooperated into `base_glyph'.          */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    FreeType error code.  0 means success.  Returns an error if        */
+  /*    color_index is invalid or reallocation fails.                      */
+  /*                                                                       */
+  typedef FT_Error
+  (*TT_Blend_Colr_Func)( TT_Face       face,
+                         FT_Int        color_index,
+                         FT_GlyphSlot  base_glyph,
+                         FT_GlyphSlot  new_glyph );
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
   /*    TT_Get_Name_Func                                                   */
   /*                                                                       */
   /* <Description>                                                         */
@@ -616,6 +678,11 @@
     TT_Set_SBit_Strike_Func      set_sbit_strike;
     TT_Load_Strike_Metrics_Func  load_strike_metrics;
 
+    TT_Load_Table_Func           load_colr;
+    TT_Free_Table_Func           free_colr;
+    TT_Load_Colr_Layer_Func      load_colr_layer;
+    TT_Blend_Colr_Func           colr_blend;
+
     TT_Get_Metrics_Func          get_metrics;
 
     TT_Get_Name_Func             get_name;
@@ -658,6 +725,10 @@
           free_eblc_,                    \
           set_sbit_strike_,              \
           load_strike_metrics_,          \
+          load_colr_,                    \
+          free_colr_,                    \
+          load_colr_layer_,              \
+          colr_blend_,                   \
           get_metrics_,                  \
           get_name_,                     \
           get_name_id_ )                 \
@@ -691,6 +762,10 @@
     free_eblc_,                          \
     set_sbit_strike_,                    \
     load_strike_metrics_,                \
+    load_colr_,                          \
+    free_colr_,                          \
+    load_colr_layer_,                    \
+    colr_blend_,                         \
     get_metrics_,                        \
     get_name_,                           \
     get_name_id_                         \
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -1352,6 +1352,10 @@
   /*                            exposed by the API and the indices used in */
   /*                            the font's sbit table.                     */
   /*                                                                       */
+  /*    colr_and_cpal        :: A pointer to data related to `COLR' and    */
+  /*                            `CPAL' tables.  NULL if tables are not     */
+  /*                            available.                                 */
+  /*                                                                       */
   /*    kern_table           :: A pointer to the `kern' table.             */
   /*                                                                       */
   /*    kern_table_size      :: The size of the `kern' table.              */
@@ -1535,6 +1539,7 @@
     FT_UInt               sbit_num_strikes;
     FT_UInt*              sbit_strike_map;
 
+    void*                 colr_and_cpal;
     FT_Byte*              kern_table;
     FT_ULong              kern_table_size;
     FT_UInt               num_kern_tables;
--- a/include/freetype/tttags.h
+++ b/include/freetype/tttags.h
@@ -46,6 +46,8 @@
 #define TTAG_CFF2  FT_MAKE_TAG( 'C', 'F', 'F', '2' )
 #define TTAG_CID   FT_MAKE_TAG( 'C', 'I', 'D', ' ' )
 #define TTAG_cmap  FT_MAKE_TAG( 'c', 'm', 'a', 'p' )
+#define TTAG_COLR  FT_MAKE_TAG( 'C', 'O', 'L', 'R' )
+#define TTAG_CPAL  FT_MAKE_TAG( 'C', 'P', 'A', 'L' )
 #define TTAG_cvar  FT_MAKE_TAG( 'c', 'v', 'a', 'r' )
 #define TTAG_cvt   FT_MAKE_TAG( 'c', 'v', 't', ' ' )
 #define TTAG_DSIG  FT_MAKE_TAG( 'D', 'S', 'I', 'G' )
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -538,6 +538,16 @@
     /* free bitmap buffer if needed */
     ft_glyphslot_free_bitmap( slot );
 
+    /* free glyph color layers if needed */
+    if ( slot->internal->color_layers )
+    {
+      FT_Colr_InternalRec*  color_layers = slot->internal->color_layers;
+
+
+      FT_FREE( color_layers->layers );
+      FT_FREE( slot->internal->color_layers );
+    }
+
     /* slot->internal might be NULL in out-of-memory situations */
     if ( slot->internal )
     {
@@ -4497,6 +4507,61 @@
       break;
 
     default:
+      if ( slot->internal->color_layers != NULL )
+      {
+        FT_Face  face = slot->face;
+
+
+        error = FT_New_GlyphSlot( face, NULL );
+        if ( !error )
+        {
+          TT_Face       ttface = (TT_Face)face;
+          SFNT_Service  sfnt   = (SFNT_Service)ttface->sfnt;
+
+          FT_Glyph_LayerRec*  glyph_layers =
+                                slot->internal->color_layers->layers;
+
+          FT_Int  idx;
+
+
+          for ( idx = 0;
+                idx < slot->internal->color_layers->num_layers;
+                idx++ )
+          {
+            FT_Int  load_flags;
+
+
+            load_flags  = slot->internal->color_layers->load_flags
+                          & ~FT_LOAD_COLOR;
+            load_flags |= FT_LOAD_RENDER;
+
+            error = FT_Load_Glyph( face,
+                                   glyph_layers[idx].glyph_index,
+                                   load_flags );
+            if ( error )
+              break;
+
+            error = sfnt->colr_blend( ttface,
+                                      glyph_layers[idx].color_index,
+                                      slot,
+                                      face->glyph );
+            if ( error )
+              break;
+          }
+
+          if ( !error )
+            slot->format = FT_GLYPH_FORMAT_BITMAP;
+
+          FT_Done_GlyphSlot( face->glyph );
+        }
+
+        if ( !error )
+          return error;
+
+        /* Failed to do the colored layer.  Draw outline instead. */
+        slot->format = FT_GLYPH_FORMAT_OUTLINE;
+      }
+
       {
         FT_ListNode  node = NULL;
 
--- a/src/sfnt/Jamfile
+++ b/src/sfnt/Jamfile
@@ -27,6 +27,7 @@
                ttmtx
                ttpost
                ttsbit
+               ttcolr
                ;
   }
   else
--- a/src/sfnt/rules.mk
+++ b/src/sfnt/rules.mk
@@ -32,6 +32,7 @@
                 $(SFNT_DIR)/ttmtx.c    \
                 $(SFNT_DIR)/ttcmap.c   \
                 $(SFNT_DIR)/ttsbit.c   \
+                $(SFNT_DIR)/ttcolr.c   \
                 $(SFNT_DIR)/ttpost.c   \
                 $(SFNT_DIR)/ttkern.c   \
                 $(SFNT_DIR)/ttbdf.c    \
--- a/src/sfnt/sfdriver.c
+++ b/src/sfnt/sfdriver.c
@@ -32,6 +32,10 @@
 #include "ttsbit.h"
 #endif
 
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+#include "ttcolr.h"
+#endif
+
 #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
 #include "ttpost.h"
 #endif
@@ -1185,6 +1189,12 @@
 #define PUT_EMBEDDED_BITMAPS( a )  NULL
 #endif
 
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+#define PUT_COLOR_LAYERS( a )  a
+#else
+#define PUT_COLOR_LAYERS( a )  NULL
+#endif
+
 #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
 #define PUT_PS_NAMES( a )  a
 #else
@@ -1243,9 +1253,18 @@
                             /* TT_Free_Table_Func      free_eblc       */
 
     PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike     ),
-                            /* TT_Set_SBit_Strike_Func set_sbit_strike */
+                   /* TT_Set_SBit_Strike_Func      set_sbit_strike     */
     PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ),
-                    /* TT_Load_Strike_Metrics_Func load_strike_metrics */
+                   /* TT_Load_Strike_Metrics_Func  load_strike_metrics */
+
+    PUT_COLOR_LAYERS( tt_face_load_colr ),
+                            /* TT_Load_Table_Func      load_colr       */
+    PUT_COLOR_LAYERS( tt_face_free_colr ),
+                            /* TT_Free_Table_Func      free_colr       */
+    PUT_COLOR_LAYERS( tt_face_load_colr_layers ),
+                            /* TT_Load_Colr_Layer_Func load_colr_layer */
+    PUT_COLOR_LAYERS( tt_face_colr_blend_layer ),
+                            /* TT_Blend_Colr_Func      colr_blend      */
 
     tt_face_get_metrics,    /* TT_Get_Metrics_Func     get_metrics     */
 
--- a/src/sfnt/sfnt.c
+++ b/src/sfnt/sfnt.c
@@ -24,6 +24,8 @@
 #include "sfobjs.c"
 #include "ttbdf.c"
 #include "ttcmap.c"
+#include "ttcolr.c"
+
 #include "ttkern.c"
 #include "ttload.c"
 #include "ttmtx.c"
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -1341,6 +1341,12 @@
     if ( sfnt->load_eblc )
       LOAD_( eblc );
 
+    if ( sfnt->load_colr )
+    {
+      /* Ignore error.  Missing optional colr/cpal is okay. */
+      LOAD_( colr );
+    }
+
     /* consider the pclt, kerning, and gasp tables as optional */
     LOAD_( pclt );
     LOAD_( gasp );
@@ -1394,7 +1400,8 @@
       /* Compute face flags.                                               */
       /*                                                                   */
       if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_CBLC ||
-           face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX )
+           face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX ||
+           face->colr_and_cpal                              )
         flags |= FT_FACE_FLAG_COLOR;      /* color glyphs */
 
       if ( has_outline == TRUE )
@@ -1737,6 +1744,10 @@
       /* destroy the embedded bitmaps table if it is loaded */
       if ( sfnt->free_eblc )
         sfnt->free_eblc( face );
+
+      /* destroy color table data if it is loaded */
+      if ( sfnt->free_colr )
+        sfnt->free_colr( face );
     }
 
 #ifdef TT_CONFIG_OPTION_BDF
--- /dev/null
+++ b/src/sfnt/ttcolr.c
@@ -1,0 +1,546 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ttcolr.c                                                               */
+/*                                                                         */
+/*    TrueType and OpenType color outline support.                         */
+/*                                                                         */
+/*  Copyright 2018 by                                                      */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  Written by Shao Yu Zhang <[email protected]>.                           */
+/*                                                                         */
+/*  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.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* `COLR' and `CPAL' table specification:                                */
+  /*                                                                       */
+  /*   https://www.microsoft.com/typography/otspec/colr.htm                */
+  /*   https://www.microsoft.com/typography/otspec/cpal.htm                */
+  /*                                                                       */
+  /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+
+#include "ttcolr.h"
+
+
+  /* NOTE: These are the table sizes calculated through the specs. */
+#define BASE_GLYPH_SIZE            6
+#define LAYER_SIZE                 4
+#define COLR_HEADER_SIZE          14
+#define CPAL_V0_HEADER_BASE_SIZE  12
+#define COLOR_SIZE                 4
+
+
+  typedef struct BaseGlyphRecord_
+  {
+    FT_UShort  gid;
+    FT_UShort  first_layer_index;
+    FT_UShort  num_layers;
+
+  } BaseGlyphRecord;
+
+
+  typedef struct Colr_
+  {
+    FT_UShort  version;
+    FT_UShort  num_base_glyphs;
+    FT_UShort  num_layers;
+
+    FT_Byte*  base_glyphs;
+    FT_Byte*  layers;
+
+  } Colr;
+
+
+  typedef struct Cpal_
+  {
+    FT_UShort  version;        /* Table version number (0 or 1 supported). */
+    FT_UShort  num_palettes_entries;      /* # of entries in each palette. */
+    FT_UShort  num_palettes;                /* # of palettes in the table. */
+    FT_UShort  num_colors;               /* Total number of color records, */
+                                         /* combined for all palettes.     */
+    FT_Byte*  colors;                              /* RGBA array of colors */
+    FT_Byte*  color_indices; /* Index of each palette's first color record */
+                             /* in the combined color record array.        */
+
+    /* version 1 fields */
+    FT_ULong*   palette_types;
+    FT_UShort*  palette_labels;
+    FT_UShort*  palette_entry_labels;
+
+  } Cpal;
+
+
+  typedef struct ColrCpal_
+  {
+    /* Accessors into the colr/cpal tables. */
+    Colr  colr;
+    Cpal  cpal;
+
+    /* The memory which backs up colr/cpal tables. */
+    void*  colr_table;
+    void*  cpal_table;
+
+  } ColrCpal;
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
+  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
+  /* messages during execution.                                            */
+  /*                                                                       */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  trace_ttcolrcpal
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_colr( TT_Face    face,
+                     FT_Stream  stream )
+  {
+    FT_Error   error;
+    FT_Memory  memory = face->root.memory;
+
+    FT_Byte*  colr_table = NULL;
+    FT_Byte*  cpal_table = NULL;
+    FT_Byte*  p          = NULL;
+
+    Colr       colr;
+    Cpal       cpal;
+    ColrCpal*  cc = NULL;
+
+    FT_ULong  base_glyph_begin, base_glyph_end, layer_begin, layer_end;
+    FT_ULong  colors_begin, colors_end;
+    FT_ULong  table_size;
+
+
+    face->colr_and_cpal = NULL;
+
+    error = face->goto_table( face, TTAG_COLR, stream, &table_size );
+    if ( error )
+      goto NoColor;
+
+    if ( table_size < sizeof ( COLR_HEADER_SIZE ) )
+      goto InvalidTable;
+
+    if ( FT_FRAME_EXTRACT( table_size, colr_table ) )
+      goto NoColor;
+
+    p = colr_table;
+
+    FT_ZERO( &colr );
+    colr.version         = FT_NEXT_USHORT( p );
+    colr.num_base_glyphs = FT_NEXT_USHORT( p );
+
+    base_glyph_begin = FT_NEXT_ULONG( p );
+    layer_begin      = FT_NEXT_ULONG( p );
+
+    colr.num_layers  = FT_NEXT_USHORT( p );
+    colr.base_glyphs = (FT_Byte*)( colr_table + base_glyph_begin );
+    colr.layers      = (FT_Byte*)( colr_table + layer_begin      );
+
+    if ( colr.version != 0 )
+      goto InvalidTable;
+
+    /* Ensure variable length tables lies within the COLR table.      */
+    /* We wrap around FT_ULong at most once since count is FT_UShort. */
+
+    base_glyph_end = base_glyph_begin +
+                     colr.num_base_glyphs * BASE_GLYPH_SIZE;
+    layer_end      = layer_begin +
+                     colr.num_layers * LAYER_SIZE;
+    if ( base_glyph_end < base_glyph_begin || base_glyph_end > table_size ||
+         layer_end      < layer_begin      || layer_end      > table_size )
+      goto InvalidTable;
+
+    /* Ensure pointers don't wrap. */
+    if ( colr.base_glyphs < colr_table || colr.layers < colr_table )
+      goto InvalidTable;
+
+    error = face->goto_table( face, TTAG_CPAL, stream, &table_size );
+    if ( error )
+      goto NoColor;
+
+    if ( table_size < CPAL_V0_HEADER_BASE_SIZE )
+      goto InvalidTable;
+
+    if ( FT_FRAME_EXTRACT( table_size, cpal_table ) )
+      goto NoColor;
+
+    p = cpal_table;
+
+    FT_ZERO( &cpal );
+    cpal.version              = FT_NEXT_USHORT( p );
+    cpal.num_palettes_entries = FT_NEXT_USHORT( p );
+    cpal.num_palettes         = FT_NEXT_USHORT( p );
+    cpal.num_colors           = FT_NEXT_USHORT( p );
+
+    colors_begin = FT_NEXT_ULONG( p );
+
+    cpal.color_indices = p;
+    cpal.colors        = (FT_Byte*)cpal_table + colors_begin;
+
+    if ( cpal.version != 0 && cpal.version != 1 )
+      goto InvalidTable;
+
+    colors_end = colors_begin + cpal.num_colors * COLOR_SIZE;
+
+    /* Ensure variable length tables lies within the COLR table.      */
+    /* We wrap around FT_ULong at most once since count is FT_UShort. */
+    if ( colors_end < colors_begin || colors_end > table_size )
+      goto InvalidTable;
+
+    if ( cpal.colors < cpal_table )
+      goto InvalidTable;
+
+    if ( FT_NEW( cc ) )
+      goto NoColor;
+
+    cc->colr       = colr;
+    cc->cpal       = cpal;
+    cc->colr_table = colr_table;
+    cc->cpal_table = cpal_table;
+
+    face->colr_and_cpal = cc;
+
+    return FT_Err_Ok;
+
+  InvalidTable:
+    error = FT_THROW( Invalid_File_Format );
+
+  NoColor:
+    if ( colr_table )
+      FT_FRAME_RELEASE( colr_table );
+    if ( cpal_table )
+      FT_FRAME_RELEASE( cpal_table );
+
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_face_free_colr( TT_Face  face )
+  {
+    FT_Stream  stream = face->root.stream;
+    FT_Memory  memory = face->root.memory;
+
+    ColrCpal*  colr_and_cpal = (ColrCpal*)face->colr_and_cpal;
+
+
+    if ( colr_and_cpal )
+    {
+      FT_FRAME_RELEASE( colr_and_cpal->colr_table );
+      FT_FRAME_RELEASE( colr_and_cpal->cpal_table );
+
+      FT_FREE( face->colr_and_cpal );
+    }
+  }
+
+
+  static FT_Bool
+  find_base_glyph_record( FT_Byte*          base_glyph_begin,
+                          FT_Int            num_base_glyph,
+                          FT_UShort         glyph_id,
+                          BaseGlyphRecord*  record )
+  {
+    FT_Int  min = 0;
+    FT_Int  max = num_base_glyph - 1;
+
+
+    while ( min <= max )
+    {
+      FT_Int    mid = min + ( max - min ) / 2;
+      FT_Byte*  p   = base_glyph_begin + mid * BASE_GLYPH_SIZE;
+
+      FT_UShort  gid  = FT_NEXT_USHORT( p );
+
+
+      if ( gid < glyph_id )
+        min = mid + 1;
+      else if (gid > glyph_id )
+        max = mid - 1;
+      else
+      {
+        record->gid               = gid;
+        record->first_layer_index = FT_NEXT_USHORT( p );
+        record->num_layers        = FT_NEXT_USHORT( p );
+
+        return 1;
+      }
+    }
+
+    return 0;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_colr_layers( TT_Face              face,
+                            FT_Int               glyph_id,
+                            FT_Glyph_LayerRec*  *ret_layers,
+                            FT_UShort*           ret_num_layers )
+  {
+    FT_Error   error;
+    FT_Memory  memory = face->root.memory;
+
+    ColrCpal*  colr_and_cpal = (ColrCpal *)face->colr_and_cpal;
+    Colr*      colr          = &colr_and_cpal->colr;
+    Cpal*      cpal          = &colr_and_cpal->cpal;
+
+    BaseGlyphRecord     glyph_record;
+    FT_Glyph_LayerRec*  layers;
+    int                 layer_idx;
+    FT_Byte*            layer_record_ptr;
+
+
+    if ( !ret_layers || !ret_num_layers )
+      return FT_THROW( Invalid_Argument );
+
+    if ( !find_base_glyph_record( colr->base_glyphs,
+                                  colr->num_base_glyphs,
+                                  glyph_id,
+                                  &glyph_record ) )
+      return FT_THROW ( Invalid_Table );
+
+    /* Load all colors for the glyphs; this would be stored in the slot. */
+    layer_record_ptr = colr->layers +
+                       glyph_record.first_layer_index * LAYER_SIZE;
+
+    if ( FT_NEW_ARRAY( layers, glyph_record.num_layers ) )
+      goto Error;
+
+    for ( layer_idx = 0; layer_idx < glyph_record.num_layers; layer_idx++ )
+    {
+      FT_UShort  gid           = FT_NEXT_USHORT( layer_record_ptr );
+      FT_UShort  palette_index = FT_NEXT_USHORT( layer_record_ptr );
+
+
+      if ( palette_index != 0xFFFF                     &&
+           palette_index >= cpal->num_palettes_entries )
+      {
+        error = FT_THROW( Invalid_File_Format );
+        goto Error;
+      }
+
+      layers[layer_idx].color_index = palette_index;
+      layers[layer_idx].glyph_index = gid;
+    }
+
+    *ret_layers     = layers;
+    *ret_num_layers = glyph_record.num_layers;
+
+    return FT_Err_Ok;
+
+  Error:
+    if ( layers )
+      FT_FREE( layers );
+
+    return error;
+  }
+
+
+  static FT_Bool
+  tt_face_find_color( TT_Face    face,
+                      FT_UShort  color_index,
+                      FT_Byte*   blue,
+                      FT_Byte*   green,
+                      FT_Byte*   red,
+                      FT_Byte*   alpha )
+  {
+    ColrCpal*  colr_and_cpal = (ColrCpal *)face->colr_and_cpal;
+    Cpal*      cpal          = &colr_and_cpal->cpal;
+
+    FT_Int    palette_index = 0;
+    FT_Byte*  p;
+    FT_Int    color_offset;
+
+
+    if ( color_index >= cpal->num_palettes_entries )
+      return 0;
+
+    p = cpal->color_indices + palette_index * sizeof ( FT_UShort );
+
+    color_offset = FT_NEXT_USHORT( p );
+
+    p = cpal->colors + color_offset + COLOR_SIZE * color_index;
+
+    *red   = FT_NEXT_BYTE( p );
+    *green = FT_NEXT_BYTE( p );
+    *blue  = FT_NEXT_BYTE( p );
+    *alpha = FT_NEXT_BYTE( p );
+
+    return 1;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_colr_blend_layer( TT_Face       face,
+                            FT_Int        color_index,
+                            FT_GlyphSlot  dstSlot,
+                            FT_GlyphSlot  srcSlot )
+  {
+    FT_Error  error;
+
+    FT_UInt  x, y;
+    FT_Byte  b, g, r, alpha;
+
+    FT_Long   size;
+    FT_Byte*  src;
+    FT_Byte*  dst;
+
+
+    if ( !dstSlot->bitmap.buffer )
+    {
+      /* Initialize destination of color bitmap */
+      /* with the size of first component.      */
+      dstSlot->bitmap_left = srcSlot->bitmap_left;
+      dstSlot->bitmap_top  = srcSlot->bitmap_top;
+
+      dstSlot->bitmap.width      = srcSlot->bitmap.width;
+      dstSlot->bitmap.rows       = srcSlot->bitmap.rows;
+      dstSlot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+      dstSlot->bitmap.pitch      = dstSlot->bitmap.width * 4;
+      dstSlot->bitmap.num_grays  = 256;
+
+      size = dstSlot->bitmap.rows * dstSlot->bitmap.pitch;
+
+      error = ft_glyphslot_alloc_bitmap( dstSlot, size );
+      if ( error )
+        return error;
+
+      FT_MEM_ZERO( dstSlot->bitmap.buffer, size );
+    }
+    else
+    {
+      /* Resize destination if needed such that new component fits. */
+      FT_Int  x_min, x_max, y_min, y_max;
+
+
+      x_min = FT_MIN( dstSlot->bitmap_left, srcSlot->bitmap_left );
+      x_max = FT_MAX( dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width,
+                      srcSlot->bitmap_left + (FT_Int)srcSlot->bitmap.width );
+
+      y_min = FT_MIN( dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows,
+                      srcSlot->bitmap_top - (FT_Int)srcSlot->bitmap.rows );
+      y_max = FT_MAX( dstSlot->bitmap_top, srcSlot->bitmap_top );
+
+      if ( x_min != dstSlot->bitmap_left                                 ||
+           x_max != dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width ||
+           y_min != dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows   ||
+           y_max != dstSlot->bitmap_top                                  )
+      {
+        FT_Memory  memory = face->root.memory;
+
+        FT_UInt  width = x_max - x_min;
+        FT_UInt  rows  = y_max - y_min;
+        FT_UInt  pitch = width * 4;
+
+        FT_Byte*  buf;
+        FT_Byte*  p;
+        FT_Byte*  q;
+
+
+        size  = rows * pitch;
+        if ( FT_ALLOC( buf, size ) )
+          return error;
+
+        p = dstSlot->bitmap.buffer;
+        q = buf +
+            pitch * ( y_max - dstSlot->bitmap_top ) +
+            4 * ( dstSlot->bitmap_left - x_min );
+
+        for ( y = 0; y < dstSlot->bitmap.rows; y++ )
+        {
+          FT_MEM_COPY( q, p, dstSlot->bitmap.width * 4 );
+
+          p += dstSlot->bitmap.pitch;
+          q += pitch;
+        }
+
+        ft_glyphslot_set_bitmap( dstSlot, buf );
+
+        dstSlot->bitmap_top  = y_max;
+        dstSlot->bitmap_left = x_min;
+
+        dstSlot->bitmap.width = width;
+        dstSlot->bitmap.rows  = rows;
+        dstSlot->bitmap.pitch = pitch;
+
+        dstSlot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+        dstSlot->format           = FT_GLYPH_FORMAT_BITMAP;
+      }
+    }
+
+    /* Default assignments to pacify compiler. */
+    r = g = b = 0;
+    alpha = 255;
+
+    if ( color_index != 0xFFFF )
+      tt_face_find_color( face, color_index, &b, &g, &r, &alpha );
+    else
+    {
+      /* TODO. foreground color from argument?                */
+      /* Add public FT_Render_Glyph_Color() with color value? */
+    }
+
+    /* XXX Convert if srcSlot.bitmap is not grey? */
+    src = srcSlot->bitmap.buffer;
+    dst = dstSlot->bitmap.buffer +
+          dstSlot->bitmap.pitch * ( dstSlot->bitmap_top - srcSlot->bitmap_top ) +
+          4 * ( srcSlot->bitmap_left - dstSlot->bitmap_left );
+
+    for ( y = 0; y < srcSlot->bitmap.rows; y++ )
+    {
+      for ( x = 0; x < srcSlot->bitmap.width; x++ )
+      {
+        int  aa = src[x];
+        int  fa = alpha * aa / 255;
+
+        int  fb = b * fa / 255;
+        int  fg = g * fa / 255;
+        int  fr = r * fa / 255;
+
+        int  ba2 = 255 - fa;
+
+        int  bb = dst[4 * x + 0];
+        int  bg = dst[4 * x + 1];
+        int  br = dst[4 * x + 2];
+        int  ba = dst[4 * x + 3];
+
+
+        dst[4 * x + 0] = bb * ba2 / 255 + fb;
+        dst[4 * x + 1] = bg * ba2 / 255 + fg;
+        dst[4 * x + 2] = br * ba2 / 255 + fr;
+        dst[4 * x + 3] = ba * ba2 / 255 + fa;
+      }
+
+      src += srcSlot->bitmap.pitch;
+      dst += dstSlot->bitmap.pitch;
+    }
+
+    return FT_Err_Ok;
+  }
+
+#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+  /* ANSI C doesn't like empty source files */
+  typedef int  _tt_colr_dummy;
+
+#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+/* EOF */
--- /dev/null
+++ b/src/sfnt/ttcolr.h
@@ -1,0 +1,57 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ttsbit.h                                                               */
+/*                                                                         */
+/*    TrueType and OpenType color outline support (specification).         */
+/*                                                                         */
+/*  Copyright 2018 by                                                      */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  Written by Shao Yu Zhang <[email protected]>.                           */
+/*                                                                         */
+/*  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.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __TTCOLR_H__
+#define __TTCOLR_H__
+
+
+#include <ft2build.h>
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_colr( TT_Face    face,
+                     FT_Stream  stream );
+
+  FT_LOCAL( void )
+  tt_face_free_colr( TT_Face  face );
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_colr_layers( TT_Face              face,
+                            FT_Int               glyph_id,
+                            FT_Glyph_LayerRec*  *ret_layers,
+                            FT_UShort*           ret_num_layers );
+
+  FT_LOCAL( FT_Error )
+  tt_face_colr_blend_layer( TT_Face       face,
+                            FT_Int        color_index,
+                            FT_GlyphSlot  dstSlot,
+                            FT_GlyphSlot  srcSlot );
+
+
+FT_END_HEADER
+
+
+#endif /* __TTCOLR_H__ */
+
+/* END */
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -463,7 +463,7 @@
                       ? &ttsize->metrics
                       : &size->hinted_metrics;
 
-    /* now load the glyph outline if necessary */
+    /* now fill in the glyph slot with outline/bitmap/layered */
     error = TT_Load_Glyph( size, slot, glyph_index, load_flags );
 
     /* force drop-out mode to 2 - irrelevant now */
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -2892,6 +2892,37 @@
          size->metrics->y_ppem < 24         )
       glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
 
+    /* The outline based algorithm took care of metrics. */
+    /* Read additional color info if requested.          */
+    if ( ( load_flags & FT_LOAD_COLOR )            &&
+         ( (TT_Face)(glyph->face) )->colr_and_cpal )
+    {
+      TT_Face       face   = (TT_Face)glyph->face;
+      FT_Memory     memory = face->root.memory;
+      SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;
+
+      FT_Glyph_LayerRec*  glyph_layers;
+      FT_UShort           num_glyph_layers;
+      FT_Colr_Internal    color_layers;
+
+
+      error = sfnt->load_colr_layer( face,
+                                     glyph_index,
+                                     &glyph_layers,
+                                     &num_glyph_layers );
+      if ( error )
+        return error;
+
+      if ( FT_NEW( color_layers ) )
+        return error;
+
+      color_layers->layers     = glyph_layers;
+      color_layers->num_layers = num_glyph_layers;
+      color_layers->load_flags = load_flags;
+
+      glyph->internal->color_layers = color_layers;
+    }
+
   Exit:
 #ifdef FT_DEBUG_LEVEL_TRACE
     if ( error )