Commit c412de52fb909dcdf6e60c574d1f991a4f43cb3b

Werner Lemberg 2018-06-09T23:56:21

[sfnt] Read `CPAL' version 1 tables. * include/freetype/internal.tttypes.h: Include FT_COLOR_H. (TT_FaceRec): Add `palette' field. * src/sfnt/ttcolr.c: Include FT_COLOR_H. (Cpal): Remove all data covered by the new `palette' field in `TT_FaceRec'. (tt_face_load_colr): Updated. Read `CPAL' version 1 data. (tt_face_load_colr_layers, tt_face_find_color): Updated. * src/sfnt/sfobjs.c (sfnt_done_face): Free glyph color palette data.

diff --git a/ChangeLog b/ChangeLog
index 93c1d51..c7ccdfc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2018-06-09  Werner Lemberg  <wl@gnu.org>
+
+	[sfnt] Read `CPAL' version 1 tables.
+
+	* include/freetype/internal.tttypes.h: Include FT_COLOR_H.
+	(TT_FaceRec): Add `palette' field.
+
+	* src/sfnt/ttcolr.c: Include FT_COLOR_H.
+	(Cpal): Remove all data covered by the new `palette' field in
+	`TT_FaceRec'.
+	(tt_face_load_colr): Updated.
+	Read `CPAL' version 1 data.
+	(tt_face_load_colr_layers, tt_face_find_color): Updated.
+
+	* src/sfnt/sfobjs.c (sfnt_done_face): Free glyph color palette data.
+
 2018-06-07  Alexei Podtelezhnikov  <apodtele@gmail.com>
 
 	[base] API for Harmony LCD rendering.
diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h
index c02d76d..2d5ee0a 100644
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -24,6 +24,7 @@
 #include <ft2build.h>
 #include FT_TRUETYPE_TABLES_H
 #include FT_INTERNAL_OBJECTS_H
+#include FT_COLOR_H
 
 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
 #include FT_MULTIPLE_MASTERS_H
@@ -1657,6 +1658,9 @@ FT_BEGIN_HEADER
     /* postscript names table */
     TT_Post_NamesRec      postscript_names;
 
+    /* glyph colors */
+    FT_Palette            palette;              /* since 2.10 */
+
 
     /************************************************************************
      *
@@ -1728,7 +1732,6 @@ FT_BEGIN_HEADER
     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;
@@ -1756,6 +1759,9 @@ FT_BEGIN_HEADER
     FT_ULong              ebdt_size;
 #endif
 
+    /* since 2.10 */
+    void*                 colr_and_cpal;
+
   } TT_FaceRec;
 
 
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index 0c0e020..87749ca 100644
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -1808,6 +1808,11 @@
     FT_FREE( face->var_postscript_prefix );
 #endif
 
+    /* freeing glyph color palette data */
+    FT_FREE( face->palette.palette_name_ids );
+    FT_FREE( face->palette.palette_types );
+    FT_FREE( face->palette.palette_entry_name_ids );
+
     face->sfnt = NULL;
   }
 
diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c
index bfe8a39..ecadcdd 100644
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -32,6 +32,7 @@
 #include FT_INTERNAL_DEBUG_H
 #include FT_INTERNAL_STREAM_H
 #include FT_TRUETYPE_TAGS_H
+#include FT_COLOR_H
 
 
 #ifdef TT_CONFIG_OPTION_COLOR_LAYERS
@@ -68,22 +69,16 @@
   } Colr;
 
 
+  /* all data from `CPAL' not covered in FT_Palette */
   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;
 
 
@@ -126,11 +121,13 @@
     ColrCpal*  cc = NULL;
 
     FT_ULong  base_glyph_begin, base_glyph_end, layer_begin, layer_end;
-    FT_ULong  colors_begin, colors_end;
+    FT_ULong  colors_offset;
     FT_ULong  table_size;
 
 
-    face->colr_and_cpal = NULL;
+    /*
+     * COLR
+     */
 
     error = face->goto_table( face, TTAG_COLR, stream, &table_size );
     if ( error )
@@ -173,6 +170,10 @@
     if ( colr.base_glyphs < colr_table || colr.layers < colr_table )
       goto InvalidTable;
 
+    /*
+     * CPAL
+     */
+
     error = face->goto_table( face, TTAG_CPAL, stream, &table_size );
     if ( error )
       goto NoColor;
@@ -186,28 +187,99 @@
     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 );
+    cpal.version = FT_NEXT_USHORT( p );
+    if ( cpal.version > 1 )
+      goto InvalidTable;
 
-    colors_begin = FT_NEXT_ULONG( p );
+    face->palette.num_palette_entries = FT_NEXT_USHORT( p );
+    face->palette.num_palettes        = FT_NEXT_USHORT( p );
 
-    cpal.color_indices = p;
-    cpal.colors        = (FT_Byte*)cpal_table + colors_begin;
+    cpal.num_colors = FT_NEXT_USHORT( p );
+    colors_offset   = FT_NEXT_ULONG( p );
 
-    if ( cpal.version != 0 && cpal.version != 1 )
+    if ( colors_offset >= table_size )
+      goto InvalidTable;
+    if ( cpal.num_colors * COLOR_SIZE > table_size - colors_offset )
       goto InvalidTable;
 
-    colors_end = colors_begin + cpal.num_colors * COLOR_SIZE;
+    cpal.color_indices = p;
+    cpal.colors        = (FT_Byte*)cpal_table + colors_offset;
 
-    /* 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.version == 1 )
+    {
+      FT_ULong    type_offset, label_offset, entry_label_offset;
+      FT_UShort*  array;
+      FT_UShort*  limit;
+      FT_UShort*  q;
 
-    if ( cpal.colors < cpal_table )
-      goto InvalidTable;
+
+      p += face->palette.num_palettes * 2;
+
+      type_offset        = FT_NEXT_ULONG( p );
+      label_offset       = FT_NEXT_ULONG( p );
+      entry_label_offset = FT_NEXT_ULONG( p );
+
+      if ( type_offset )
+      {
+        if ( type_offset >= table_size )
+          goto InvalidTable;
+        if ( face->palette.num_palettes * 2 > table_size - type_offset )
+          goto InvalidTable;
+
+        if ( FT_QNEW_ARRAY( array, face->palette.num_palettes ) )
+          goto NoColor;
+
+        p     = cpal_table + type_offset;
+        q     = array;
+        limit = q + face->palette.num_palettes * sizeof ( FT_UShort );
+
+        while ( q < limit )
+          *q++ = FT_NEXT_USHORT( p );
+
+        face->palette.palette_types = array;
+      }
+
+      if ( label_offset )
+      {
+        if ( label_offset >= table_size )
+          goto InvalidTable;
+        if ( face->palette.num_palettes * 2 > table_size - label_offset )
+          goto InvalidTable;
+
+        if ( FT_QNEW_ARRAY( array, face->palette.num_palettes ) )
+          goto NoColor;
+
+        p     = cpal_table + label_offset;
+        q     = array;
+        limit = q + face->palette.num_palettes * sizeof ( FT_UShort );
+
+        while ( q < limit )
+          *q++ = FT_NEXT_USHORT( p );
+
+        face->palette.palette_name_ids = array;
+      }
+
+      if ( entry_label_offset )
+      {
+        if ( entry_label_offset >= table_size )
+          goto InvalidTable;
+        if ( face->palette.num_palette_entries * 2 >
+               table_size - entry_label_offset )
+          goto InvalidTable;
+
+        if ( FT_QNEW_ARRAY( array, face->palette.num_palette_entries ) )
+          goto NoColor;
+
+        p     = cpal_table + entry_label_offset;
+        q     = array;
+        limit = q + face->palette.num_palette_entries * sizeof ( FT_UShort );
+
+        while ( q < limit )
+          *q++ = FT_NEXT_USHORT( p );
+
+        face->palette.palette_entry_name_ids = array;
+      }
+    }
 
     if ( FT_NEW( cc ) )
       goto NoColor;
@@ -225,10 +297,10 @@
     error = FT_THROW( Invalid_File_Format );
 
   NoColor:
-    if ( colr_table )
-      FT_FRAME_RELEASE( colr_table );
-    if ( cpal_table )
-      FT_FRAME_RELEASE( cpal_table );
+    FT_FRAME_RELEASE( colr_table );
+    FT_FRAME_RELEASE( cpal_table );
+
+    /* arrays in `face->palette' are freed in `sfnt_face_done' */
 
     return error;
   }
@@ -300,7 +372,6 @@
 
     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_Layer   layers;
@@ -335,8 +406,8 @@
       FT_UShort  palette_index = FT_NEXT_USHORT( layer_record_ptr );
 
 
-      if ( palette_index != 0xFFFF                     &&
-           palette_index >= cpal->num_palettes_entries )
+      if ( palette_index != 0xFFFF                            &&
+           palette_index >= face->palette.num_palette_entries )
       {
         error = FT_THROW( Invalid_File_Format );
         goto Error;
@@ -375,7 +446,7 @@
     FT_Int    color_offset;
 
 
-    if ( color_index >= cpal->num_palettes_entries )
+    if ( color_index >= face->palette.num_palette_entries )
       return 0;
 
     p = cpal->color_indices + palette_index * (int)sizeof ( FT_UShort );