Commit cdad4db4240044512a33cf480f28b093ae3a85c9

Dominik Röttsches 2020-12-16T16:52:24

[sfnt] Add API to get actual paint from `FT_OpaquePaint` (#59703). * src/sfnt/ttcolr.c (tt_face_get_paint): New function to resolve an `FT_OpaquePaint` paint reference into an `FT_COLR_Paint` object of a certain format, which contains the detailed information stored in a paint of the respective format. (read_paint): New function to provide the format specific parsing and to populate the data members of each specific `FT_COLR_Paint` subtype. (read_color_line): New function to parse retrieved color line information into an `FT_ColorLine` object, which has information about the color line extend mode as well as an `FT_ColorStopIterator` object. * src/sfnt/ttcolr.h: Updated.

diff --git a/ChangeLog b/ChangeLog
index c4c3b3a..a33c1e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
 2020-12-16  Dominik Röttsches  <drott@chromium.org>
 
+	[sfnt] Add API to get actual paint from `FT_OpaquePaint` (#59703).
+
+	* src/sfnt/ttcolr.c (tt_face_get_paint): New function to resolve an
+	`FT_OpaquePaint` paint reference into an `FT_COLR_Paint` object of a
+	certain format, which contains the detailed information stored in a
+	paint of the respective format.
+	(read_paint): New function to provide the format specific parsing
+	and to populate the data members of each specific `FT_COLR_Paint`
+	subtype.
+	(read_color_line): New function to parse retrieved color line
+	information into an `FT_ColorLine` object, which has information
+	about the color line extend mode as well as an
+	`FT_ColorStopIterator` object.
+
+	* src/sfnt/ttcolr.h: Updated.
+
+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.
diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c
index 9b05b3a..0d2b866 100644
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -320,6 +320,301 @@
 
 
   static FT_Bool
+  read_color_line( FT_Byte*      paint_base,
+                   FT_ULong      colorline_offset,
+                   FT_ColorLine  *colorline )
+  {
+    FT_Byte*        p = (FT_Byte *)( paint_base + colorline_offset );
+    FT_PaintExtend  paint_extend;
+    /* TODO: Check pointer limits. */
+
+
+    paint_extend = FT_NEXT_BYTE( p );
+    if ( paint_extend > FT_COLR_PAINT_EXTEND_REFLECT )
+      return 0;
+
+    colorline->extend = paint_extend;
+
+    colorline->color_stop_iterator.num_color_stops    = FT_NEXT_USHORT( p );
+    colorline->color_stop_iterator.p                  = p;
+    colorline->color_stop_iterator.current_color_stop = 0;
+
+    return 1;
+  }
+
+
+  static FT_Bool
+  read_paint( Colr*           colr,
+              FT_Byte*        p,
+              FT_COLR_Paint*  apaint )
+  {
+    FT_Byte*  paint_base = p;
+
+
+    apaint->format = FT_NEXT_BYTE( p );
+
+    if ( apaint->format >= FT_COLR_PAINT_FORMAT_MAX )
+      return 0;
+
+    if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_LAYERS )
+    {
+      /* Initialize layer iterator/ */
+      FT_Byte    num_layers;
+      FT_UInt32  first_layer_index;
+
+
+      num_layers = FT_NEXT_BYTE( p );
+      if ( num_layers > colr->num_layers_v1 )
+        return 0;
+
+      first_layer_index = FT_NEXT_ULONG( p );
+      if ( first_layer_index + num_layers > colr->num_layers_v1 )
+        return 0;
+
+      apaint->u.colr_layers.layer_iterator.num_layers = num_layers;
+      apaint->u.colr_layers.layer_iterator.layer      = 0;
+      /* TODO: Check whether pointer is outside colr? */
+      apaint->u.colr_layers.layer_iterator.p =
+        colr->layers_v1 +
+        LAYER_V1_LIST_NUM_LAYERS_SIZE +
+        LAYER_V1_LIST_PAINT_OFFSET_SIZE * first_layer_index;
+
+      return 1;
+    }
+
+    if ( apaint->format == FT_COLR_PAINTFORMAT_GLYPH )
+    {
+      FT_UInt32  paint_offset;
+      FT_Byte*   paint_p;
+
+
+      paint_offset = FT_NEXT_UOFF3( p );
+      if ( !paint_offset )
+        return 0;
+
+      paint_p = (FT_Byte*)( paint_base + paint_offset );
+      if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.glyph.paint.p = paint_p;
+      apaint->u.glyph.glyphID = FT_NEXT_USHORT( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_SOLID )
+    {
+      apaint->u.solid.color.palette_index = FT_NEXT_USHORT ( p );
+      apaint->u.solid.color.alpha         = FT_NEXT_USHORT ( p );
+      /* skip VarIdx */
+      FT_NEXT_ULONG ( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT )
+    {
+      FT_ULong  color_line_offset = FT_NEXT_OFF3( p );
+
+
+      if ( !read_color_line( paint_base,
+                             color_line_offset,
+                             &apaint->u.linear_gradient.colorline ) )
+        return 0;
+
+      /* skip VarIdx entries */
+      apaint->u.linear_gradient.p0.x = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.linear_gradient.p0.y = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.linear_gradient.p1.x = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.linear_gradient.p1.y = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.linear_gradient.p2.x = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.linear_gradient.p2.y = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT )
+    {
+      FT_ULong  color_line_offset = color_line_offset = FT_NEXT_OFF3( p );
+
+
+      if ( !read_color_line( paint_base,
+                             color_line_offset,
+                             &apaint->u.linear_gradient.colorline ) )
+        return 0;
+
+      /* skip VarIdx entries */
+      apaint->u.radial_gradient.c0.x = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.radial_gradient.c0.y = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+
+      apaint->u.radial_gradient.r0 = FT_NEXT_USHORT ( p );
+      FT_NEXT_ULONG ( p );
+
+      apaint->u.radial_gradient.c1.x = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+      apaint->u.radial_gradient.c1.y = FT_NEXT_SHORT ( p );
+      FT_NEXT_ULONG ( p );
+
+      apaint->u.radial_gradient.r1 = FT_NEXT_USHORT ( p );
+      FT_NEXT_ULONG ( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_TRANSFORMED )
+    {
+      FT_UInt32  paint_offset;
+      FT_Byte*   paint_p;
+
+
+      paint_offset = FT_NEXT_UOFF3( p );
+      if ( !paint_offset )
+        return 0;
+
+      paint_p = (FT_Byte*)( paint_base + paint_offset );
+      if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.transformed.paint.p = paint_p;
+
+      /* skip VarIdx entries */
+      apaint->u.transformed.affine.xx = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.transformed.affine.yx = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.transformed.affine.xy = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.transformed.affine.yy = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.transformed.affine.dx = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.transformed.affine.dy = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_TRANSLATE )
+    {
+      FT_UInt32  paint_offset;
+      FT_Byte*   paint_p;
+
+
+      paint_offset = FT_NEXT_UOFF3( p );
+      if ( !paint_offset )
+        return 0;
+
+      paint_p = (FT_Byte*)( paint_base + paint_offset );
+      if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.translate.paint.p = paint_p;
+
+      /* skip VarIdx entries */
+      apaint->u.translate.dx = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.translate.dy = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_ROTATE )
+    {
+      FT_UInt32  paint_offset;
+      FT_Byte*   paint_p;
+
+
+      paint_offset = FT_NEXT_UOFF3( p );
+      if ( !paint_offset )
+        return 0;
+
+      paint_p = (FT_Byte*)( paint_base + paint_offset );
+      if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.rotate.paint.p = paint_p;
+
+      /* skip VarIdx entries */
+      apaint->u.rotate.angle = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+
+      apaint->u.rotate.center_x = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.rotate.center_y = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_SKEW )
+    {
+      FT_UInt32  paint_offset;
+      FT_Byte*   paint_p;
+
+
+      paint_offset = FT_NEXT_UOFF3( p );
+      if ( !paint_offset )
+        return 0;
+
+      paint_p = (FT_Byte*)( paint_base + paint_offset );
+      if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.skew.paint.p = paint_p;
+
+      /* skip VarIdx entries */
+      apaint->u.skew.x_skew_angle = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.skew.y_skew_angle = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+
+      apaint->u.skew.center_x = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+      apaint->u.skew.center_y = FT_NEXT_LONG( p );
+      FT_NEXT_ULONG( p );
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_COMPOSITE )
+    {
+      FT_UInt32  source_paint_offset;
+      FT_Byte*   source_paint_p;
+
+      FT_UInt32  backdrop_paint_offset;
+      FT_Byte*   backdrop_paint_p;
+
+      FT_UInt    composite_mode;
+
+
+      source_paint_offset = FT_NEXT_UOFF3( p );
+      if ( !source_paint_offset )
+        return 0;
+
+      source_paint_p = (FT_Byte*)( paint_base + source_paint_offset );
+      if ( source_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.composite.source_paint.p = source_paint_p;
+
+      composite_mode = FT_NEXT_BYTE( p );
+      if ( composite_mode >= FT_COLR_COMPOSITE_MAX )
+        return 0;
+
+      apaint->u.composite.composite_mode = composite_mode;
+
+      backdrop_paint_offset = FT_NEXT_UOFF3( p );
+      if ( !backdrop_paint_offset )
+        return 0;
+
+      backdrop_paint_p = (FT_Byte*)( paint_base + backdrop_paint_offset );
+      if ( backdrop_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
+        return 0;
+
+      apaint->u.composite.backdrop_paint.p = backdrop_paint_p;
+    }
+
+    else if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_GLYPH )
+      apaint->u.colr_glyph.glyphID = FT_NEXT_USHORT( p );
+
+    return 1;
+  }
+
+
+  static FT_Bool
   find_base_glyph_v1_record ( FT_Byte *           base_glyph_begin,
                               FT_Int              num_base_glyph,
                               FT_UInt             glyph_id,
@@ -398,6 +693,26 @@
   }
 
 
+  FT_LOCAL_DEF( FT_Bool )
+  tt_face_get_paint( TT_Face         face,
+                     FT_OpaquePaint  opaque_paint,
+                     FT_COLR_Paint*  paint )
+  {
+    Colr*  colr = (Colr*)face->colr;
+
+    FT_Byte*  p;
+
+
+    if ( opaque_paint.p < (FT_Byte*)colr->table                         ||
+         opaque_paint.p >= ( (FT_Byte*)colr->table + colr->table_size ) )
+      return 0;
+
+    p = opaque_paint.p;
+
+    return read_paint( colr, p, paint );
+  }
+
+
   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 6cce50f..cdae3fa 100644
--- a/src/sfnt/ttcolr.h
+++ b/src/sfnt/ttcolr.h
@@ -47,6 +47,11 @@ FT_BEGIN_HEADER
                                 FT_UInt          base_glyph,
                                 FT_OpaquePaint*  paint );
 
+  FT_LOCAL( FT_Bool )
+  tt_face_get_paint( TT_Face         face,
+                     FT_OpaquePaint  opaque_paint,
+                     FT_COLR_Paint*  paint );
+
   FT_LOCAL( FT_Error )
   tt_face_colr_blend_layer( TT_Face       face,
                             FT_UInt       color_index,