Commit 9e422b67c8f1a2e06cb934c2867e0536729793f4

Dominik Röttsches 2020-12-16T16:44:34

[sfnt] Add API to retrieve 'COLR' v1 root paint (#59703). * src/sfnt/ttcolr.c (BaseGlyphV1Record): New structure. (tt_face_load_colr): Handle version 1 table header. (find_base_glyph_v1_record): New auxiliary function. (tt_face_get_colr_glyph_paint): New function to find the root `FT_OpaquePaint` object for a given glyph ID. * src/sfnt/ttcolr.h: Updated.

diff --git a/ChangeLog b/ChangeLog
index 871594e..c4c3b3a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2020-12-16  Dominik Röttsches  <drott@chromium.org>
 
+	[sfnt] Add API to retrieve 'COLR' v1 root paint (#59703).
+
+	* src/sfnt/ttcolr.c (BaseGlyphV1Record): New structure.
+	(tt_face_load_colr): Handle version 1 table header.
+	(find_base_glyph_v1_record): New auxiliary function.
+	(tt_face_get_colr_glyph_paint): New function to find the root
+	`FT_OpaquePaint` object for a given glyph ID.
+
+	* src/sfnt/ttcolr.h: Updated.
+
+2020-12-16  Dominik Röttsches  <drott@chromium.org>
+
 	Add new methods required for 'COLR' v1 to public API (#59703).
 
 	* include/freetype/freetype.h (FT_Get_Color_Glyph_Paint): New method
diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c
index 9025e35..9b05b3a 100644
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -5,7 +5,7 @@
  *   TrueType and OpenType colored glyph layer support (body).
  *
  * Copyright (C) 2018-2020 by
- * David Turner, Robert Wilhelm, and Werner Lemberg.
+ * David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg.
  *
  * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
  *
@@ -31,6 +31,7 @@
 #include <freetype/internal/ftstream.h>
 #include <freetype/tttags.h>
 #include <freetype/ftcolor.h>
+#include <freetype/config/integer-types.h>
 
 
 #ifdef TT_CONFIG_OPTION_COLOR_LAYERS
@@ -39,12 +40,16 @@
 
 
   /* NOTE: These are the table sizes calculated through the specs. */
-#define BASE_GLYPH_SIZE            6U
-#define LAYER_SIZE                 4U
-#define COLR_HEADER_SIZE          14U
+#define BASE_GLYPH_SIZE                   6U
+#define BASE_GLYPH_V1_RECORD_SIZE         6U
+#define LAYER_V1_LIST_PAINT_OFFSET_SIZE   4U
+#define LAYER_V1_LIST_NUM_LAYERS_SIZE     4U
+#define COLOR_STOP_SIZE                   6U
+#define LAYER_SIZE                        4U
+#define COLR_HEADER_SIZE                 14U
 
 
-  typedef struct BaseGlyphRecord_
+  typedef struct  BaseGlyphRecord_
   {
     FT_UShort  gid;
     FT_UShort  first_layer_index;
@@ -53,7 +58,16 @@
   } BaseGlyphRecord;
 
 
-  typedef struct Colr_
+  typedef struct  BaseGlyphV1Record_
+  {
+    FT_UShort  gid;
+    /* Offset from start of BaseGlyphV1List, i.e., from base_glyphs_v1. */
+    FT_ULong   paint_offset;
+
+  } BaseGlyphV1Record;
+
+
+  typedef struct  Colr_
   {
     FT_UShort  version;
     FT_UShort  num_base_glyphs;
@@ -62,7 +76,14 @@
     FT_Byte*  base_glyphs;
     FT_Byte*  layers;
 
-    /* The memory which backs up the `COLR' table. */
+    FT_ULong  num_base_glyphs_v1;
+    /* Points at beginning of BaseGlyphV1List. */
+    FT_Byte*  base_glyphs_v1;
+
+    FT_ULong  num_layers_v1;
+    FT_Byte*  layers_v1;
+
+    /* The memory that backs up the `COLR' table. */
     void*     table;
     FT_ULong  table_size;
 
@@ -88,10 +109,14 @@
 
     FT_Byte*  table = NULL;
     FT_Byte*  p     = NULL;
+    /* Needed for reading array lengths in referenced tables. */
+    FT_Byte*  p1    = NULL;
 
     Colr*  colr = NULL;
 
     FT_ULong  base_glyph_offset, layer_offset;
+    FT_ULong  base_glyphs_offset_v1, num_base_glyphs_v1;
+    FT_ULong  layer_offset_v1, num_layers_v1;
     FT_ULong  table_size;
 
 
@@ -115,7 +140,7 @@
       goto NoColr;
 
     colr->version = FT_NEXT_USHORT( p );
-    if ( colr->version != 0 )
+    if ( colr->version != 0 && colr->version != 1 )
       goto InvalidTable;
 
     colr->num_base_glyphs = FT_NEXT_USHORT( p );
@@ -135,6 +160,35 @@
     if ( colr->num_layers * LAYER_SIZE > table_size - layer_offset )
       goto InvalidTable;
 
+    if ( colr->version == 1 )
+    {
+      base_glyphs_offset_v1 = FT_NEXT_ULONG( p );
+
+      if ( base_glyphs_offset_v1 >= table_size )
+        goto InvalidTable;
+
+      p1                 = (FT_Byte*)( table + base_glyphs_offset_v1 );
+      num_base_glyphs_v1 = FT_PEEK_ULONG( p1 );
+
+      if ( num_base_glyphs_v1 * BASE_GLYPH_V1_RECORD_SIZE >
+             table_size - base_glyphs_offset_v1 )
+        goto InvalidTable;
+
+      colr->num_base_glyphs_v1 = num_base_glyphs_v1;
+      colr->base_glyphs_v1     = p1;
+
+      layer_offset_v1 = FT_NEXT_ULONG( p );
+
+      if ( !layer_offset_v1 || layer_offset_v1 >= table_size )
+        goto InvalidTable;
+
+      p1            = (FT_Byte*)( table + layer_offset_v1 );
+      num_layers_v1 = FT_PEEK_ULONG( p1 );
+
+      colr->num_layers_v1 = num_layers_v1;
+      colr->layers_v1     = p1;
+    }
+
     colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset );
     colr->layers      = (FT_Byte*)( table + layer_offset      );
     colr->table       = table;
@@ -265,6 +319,85 @@
   }
 
 
+  static FT_Bool
+  find_base_glyph_v1_record ( FT_Byte *           base_glyph_begin,
+                              FT_Int              num_base_glyph,
+                              FT_UInt             glyph_id,
+                              BaseGlyphV1Record  *record )
+  {
+    FT_Int  min = 0;
+    FT_Int  max = num_base_glyph - 1;
+
+
+    while ( min <= max )
+    {
+      FT_Int  mid = min + ( max - min ) / 2;
+
+      /*
+       * `base_glyph_begin` is the beginning of `BaseGlyphV1List`;
+       * skip `numBaseGlyphV1Records` by adding 4 to start binary search
+       * in the array of `BaseGlyphV1Record`.
+       */
+      FT_Byte  *p = base_glyph_begin + 4 + mid * BASE_GLYPH_V1_RECORD_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->paint_offset = FT_NEXT_ULONG ( p );
+        return 1;
+      }
+    }
+
+    return 0;
+  }
+
+
+  FT_LOCAL_DEF ( FT_Bool )
+  tt_face_get_colr_glyph_paint( TT_Face          face,
+                                FT_UInt          base_glyph,
+                                FT_OpaquePaint*  opaque_paint )
+  {
+    Colr*  colr = (Colr*)face->colr;
+
+    BaseGlyphV1Record  base_glyph_v1_record;
+    FT_Byte*           p;
+
+
+    if ( colr->version < 1 || !colr->num_base_glyphs_v1 ||
+         !colr->base_glyphs_v1 )
+      return 0;
+
+    if ( opaque_paint->p )
+      return 0;
+
+    if ( !find_base_glyph_v1_record( colr->base_glyphs_v1,
+                                     colr->num_base_glyphs_v1,
+                                     base_glyph,
+                                     &base_glyph_v1_record ) )
+      return 0;
+
+    if ( !base_glyph_v1_record.paint_offset                   ||
+         base_glyph_v1_record.paint_offset > colr->table_size )
+      return 0;
+
+    p = (FT_Byte*)( colr->base_glyphs_v1 +
+                    base_glyph_v1_record.paint_offset );
+    if ( p >= ( (FT_Byte*)colr->table + colr->table_size ) )
+      return 0;
+
+    opaque_paint->p = p;
+
+    return 1;
+  }
+
+
   FT_LOCAL_DEF( FT_Error )
   tt_face_colr_blend_layer( TT_Face       face,
                             FT_UInt       color_index,
diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h
index 6412162..6cce50f 100644
--- a/src/sfnt/ttcolr.h
+++ b/src/sfnt/ttcolr.h
@@ -42,6 +42,11 @@ FT_BEGIN_HEADER
                           FT_UInt           *acolor_index,
                           FT_LayerIterator*  iterator );
 
+  FT_LOCAL( FT_Bool )
+  tt_face_get_colr_glyph_paint( TT_Face          face,
+                                FT_UInt          base_glyph,
+                                FT_OpaquePaint*  paint );
+
   FT_LOCAL( FT_Error )
   tt_face_colr_blend_layer( TT_Face       face,
                             FT_UInt       color_index,