Commit 64f01bfeddea30b8b1f1c375df9b72d88bd5a3c5

Dominik Röttsches 2021-01-20T13:04:50

[sfnt] Provide optional root transform for 'COLR' v1 glyph graph. * include/freetype/freetype.h (FT_Get_Color_Glyph_Paint): Additional function argument root_transform to control whether root transform should be returned. (FT_OpaquePaint): Additional tracking field to denote whether root transform is to be returned. * include/freetype/internal/sfnt.h (TT_Get_Color_Glyph_Paint_Func): Propagate additional argument. * src/base/ftobjs.c (FT_Get_Color_Glyph_Paint): Ditto. * src/sfnt/ttcolr.c (tt_face_get_colr_glyph_paint): Return root transform reflecting the size and tranform configured on FT_Face. (read_paint): Initialize and track status of insert_root_transform flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
diff --git a/ChangeLog b/ChangeLog
index c92b98c..d310fac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2021-02-09  Dominik Röttsches  <drott@chromium.org>
+
+	[sfnt] Provide optional root transform for 'COLR' v1 glyph graph.
+
+        * include/freetype/freetype.h (FT_Get_Color_Glyph_Paint):
+	Additional function argument `root_transform` to control whether
+	root transform should be returned.
+	(FT_OpaquePaint): Additional tracking field to denote whether
+	root transform is to be returned.
+	* include/freetype/internal/sfnt.h
+	(TT_Get_Color_Glyph_Paint_Func): Propagate additional argument.
+	* src/base/ftobjs.c (FT_Get_Color_Glyph_Paint): Ditto.
+	* src/sfnt/ttcolr.c (tt_face_get_colr_glyph_paint): Return root
+	transform reflecting the size and tranform configured on
+	`FT_Face`.
+	(read_paint): Initialize and track status of insert_root_transform
+	flag.
+
 2021-02-09  Xavier Claessens  <xavier.claessens@collabora.com>
 
 	* meson.build: s/freetype2_dep/freetype_dep/.
diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h
index dba1f9a..5d8e8dc 100644
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -4479,11 +4479,15 @@ FT_BEGIN_HEADER
    *   p ::
    *     An internal offset to a Paint table, needs to be set to NULL before
    *     passing this struct as an argument to @FT_Get_Paint.
+   *
+   *   insert_root_transform ::
+   *     An internal boolean to track whether an initial root transform is
+   *     to be provided.  Do not set this value.
    */
   typedef struct  FT_Opaque_Paint_
   {
     FT_Byte*  p;
-
+    FT_Bool   insert_root_transform;
   } FT_OpaquePaint;
 
 
@@ -4881,6 +4885,33 @@ FT_BEGIN_HEADER
 
   /**************************************************************************
    *
+   * @enum:
+   *   FT_Color_Root_Transform
+   *
+   * @description:
+   *   An enumeration to specify whether @FT_Get_Color_Glyph_Paint is to
+   *   return a root transform to configure the client's graphics context
+   *   matrix.
+   *
+   * @values:
+   *   FT_COLOR_INCLUDE_ROOT_TRANSFORM ::
+   *     Do include the root transform as the initial @FT_COLR_Paint object.
+   *
+   *   FT_COLOR_NO_ROOT_TRANSFORM ::
+   *     Do not output an initial root transform.
+   */
+  typedef enum  FT_Color_Root_Transform_
+  {
+    FT_COLOR_INCLUDE_ROOT_TRANSFORM,
+    FT_COLOR_NO_ROOT_TRANSFORM,
+
+    FT_COLOR_ROOT_TRANSFORM_MAX
+
+  } FT_Color_Root_Transform;
+
+
+  /**************************************************************************
+   *
    * @function:
    *   FT_Get_Color_Glyph_Paint
    *
@@ -4898,6 +4929,26 @@ FT_BEGIN_HEADER
    *   function and specifying a glyph ID, one retrieves the root paint
    *   table for this glyph ID.
    *
+   *   This function allows control whether an initial root transform is
+   *   returned to configure scaling, transform, and translation correctly
+   *   on the client's graphics context.  The initial root transform is
+   *   computed and returned according to the values configured for @FT_Size
+   *   and @FT_Set_Transform on the @FT_Face object, see below for details
+   *   of the `root_transform` parameter.  This has implications for a
+   *   client 'COLR' v1 implementation: When this function returns an
+   *   initially computed root transform, at the time of executing the
+   *   @FT_Paint_Glyph operation, the contours should be retrieved using
+   *   @FT_Load_Glyph at unscaled, untransformed size.  This is because the
+   *   root transform applied to the graphics context will take care of
+   *   correct scaling.
+   *
+   *   Alternatively, to allow hinting of contours, at the time of executing
+   *   @FT_Load_Glyph, the current graphics context transformation matrix
+   *   can be decomposed into a scaling matrix and a remainder, and
+   *   @FT_Load_Glyph can be used to retrieve the contours at scaled size.
+   *   Care must then be taken to blit or clip to the graphics context with
+   *   taking this remainder transformation into account.
+   *
    * @input:
    *   face ::
    *     A handle to the parent face object.
@@ -4905,6 +4956,28 @@ FT_BEGIN_HEADER
    *   base_glyph ::
    *     The glyph index for which to retrieve the root paint table.
    *
+   *   root_transform ::
+   *     Specifies whether an initially computed root is returned by
+   *     @FT_Paint_Transformed to account for the activated size (see
+   *     @FT_Activate_Size) and the configured transform and translate (see
+   *     @FT_Set_Translate).
+   *
+   *     This root transform is returned before nodes of the glyph graph of
+   *     the font are returned.  Subsequent @FT_COLR_Paint structures
+   *     contain unscaled and untransformed values.  The inserted root
+   *     transform enables the client application to apply an initial
+   *     transform to its graphics context.  When executing subsequent
+   *     FT_COLR_Paint operations, values from @FT_COLR_Paint operations
+   *     will ultimately be correctly scaled because of the root transform
+   *     applied to the graphics context.  Use
+   *     @FT_COLOR_INCLUDE_ROOT_TRANSFORM to include the root transform, use
+   *     @FT_COLOR_NO_ROOT_TRANSFORM to not include it.  The latter may be
+   *     useful when traversing the 'COLR' v1 glyph graph and reaching a
+   *     @FT_PaintColrGlyph.  When recursing into @FT_PaintColrGlyph and
+   *     painting that inline, no additional root transform is needed as it
+   *     has already been applied to the graphics context at the beginning
+   *     of drawing this glyph.
+   *
    * @output:
    *   paint ::
    *     The @FT_OpaquePaint object that references the actual paint table.
@@ -4918,9 +4991,10 @@ FT_BEGIN_HEADER
    *   error, value~0 is returned also.
    */
   FT_EXPORT( FT_Bool )
-  FT_Get_Color_Glyph_Paint( FT_Face          face,
-                            FT_UInt          base_glyph,
-                            FT_OpaquePaint*  paint );
+  FT_Get_Color_Glyph_Paint( FT_Face                  face,
+                            FT_UInt                  base_glyph,
+                            FT_Color_Root_Transform  root_transform,
+                            FT_OpaquePaint*          paint );
 
 
   /**************************************************************************
diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h
index bfb2d71..438ec89 100644
--- a/include/freetype/internal/sfnt.h
+++ b/include/freetype/internal/sfnt.h
@@ -549,9 +549,10 @@ FT_BEGIN_HEADER
    *   error, value~0 is returned also.
    */
   typedef FT_Bool
-  ( *TT_Get_Color_Glyph_Paint_Func )( TT_Face          face,
-                                      FT_UInt          base_glyph,
-                                      FT_OpaquePaint  *paint );
+  ( *TT_Get_Color_Glyph_Paint_Func )( TT_Face                   face,
+                                      FT_UInt                   base_glyph,
+                                      FT_Color_Root_Transform   root_transform,
+                                      FT_OpaquePaint           *paint );
 
 
   /**************************************************************************
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index dd478e0..829f4ab 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -5571,9 +5571,10 @@
   /* documentation is in freetype.h */
 
   FT_EXPORT_DEF ( FT_Bool )
-  FT_Get_Color_Glyph_Paint( FT_Face          face,
-                            FT_UInt          base_glyph,
-                            FT_OpaquePaint*  paint )
+  FT_Get_Color_Glyph_Paint( FT_Face                  face,
+                            FT_UInt                  base_glyph,
+                            FT_Color_Root_Transform  root_transform,
+                            FT_OpaquePaint*          paint )
   {
     TT_Face       ttface;
     SFNT_Service  sfnt;
@@ -5589,7 +5590,10 @@
     sfnt   = (SFNT_Service)ttface->sfnt;
 
     if ( sfnt->get_colr_layer )
-      return sfnt->get_colr_glyph_paint( ttface, base_glyph, paint );
+      return sfnt->get_colr_glyph_paint( ttface,
+                                         base_glyph,
+                                         root_transform,
+                                         paint );
     else
       return 0;
   }
diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c
index 8a6aef2..6ba3a63 100644
--- a/src/sfnt/ttcolr.c
+++ b/src/sfnt/ttcolr.c
@@ -396,8 +396,9 @@
       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 );
+      apaint->u.glyph.paint.p                     = paint_p;
+      apaint->u.glyph.paint.insert_root_transform = 0;
+      apaint->u.glyph.glyphID                     = FT_NEXT_USHORT( p );
     }
 
     else if ( apaint->format == FT_COLR_PAINTFORMAT_SOLID )
@@ -475,7 +476,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.transformed.paint.p = paint_p;
+      apaint->u.transformed.paint.p                     = paint_p;
+      apaint->u.transformed.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.transformed.affine.xx = FT_NEXT_LONG( p );
@@ -506,7 +508,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.translate.paint.p = paint_p;
+      apaint->u.translate.paint.p                     = paint_p;
+      apaint->u.translate.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.translate.dx = FT_NEXT_LONG( p );
@@ -529,7 +532,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.rotate.paint.p = paint_p;
+      apaint->u.rotate.paint.p                     = paint_p;
+      apaint->u.rotate.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.rotate.angle = FT_NEXT_LONG( p );
@@ -555,7 +559,8 @@
       if ( paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.skew.paint.p = paint_p;
+      apaint->u.skew.paint.p                     = paint_p;
+      apaint->u.skew.paint.insert_root_transform = 0;
 
       /* skip VarIdx entries */
       apaint->u.skew.x_skew_angle = FT_NEXT_LONG( p );
@@ -588,7 +593,10 @@
       if ( source_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.composite.source_paint.p = source_paint_p;
+      apaint->u.composite.source_paint.p =
+        source_paint_p;
+      apaint->u.composite.source_paint.insert_root_transform =
+        0;
 
       composite_mode = FT_NEXT_BYTE( p );
       if ( composite_mode >= FT_COLR_COMPOSITE_MAX )
@@ -604,7 +612,10 @@
       if ( backdrop_paint_p > ( (FT_Byte*)colr->table + colr->table_size ) )
         return 0;
 
-      apaint->u.composite.backdrop_paint.p = backdrop_paint_p;
+      apaint->u.composite.backdrop_paint.p =
+        backdrop_paint_p;
+      apaint->u.composite.backdrop_paint.insert_root_transform =
+        0;
     }
 
     else if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_GLYPH )
@@ -655,17 +666,16 @@
 
 
   FT_LOCAL_DEF ( FT_Bool )
-  tt_face_get_colr_glyph_paint( TT_Face          face,
-                                FT_UInt          base_glyph,
-                                FT_OpaquePaint*  opaque_paint )
+  tt_face_get_colr_glyph_paint( TT_Face                  face,
+                                FT_UInt                  base_glyph,
+                                FT_Color_Root_Transform  root_transform,
+                                FT_OpaquePaint*          opaque_paint )
   {
-    Colr*  colr = (Colr*)face->colr;
-
+    Colr*              colr = (Colr*)face->colr;
     BaseGlyphV1Record  base_glyph_v1_record;
     FT_Byte*           p;
 
-
-    if ( !colr )
+    if ( !colr || !colr->table )
       return 0;
 
     if ( colr->version < 1 || !colr->num_base_glyphs_v1 ||
@@ -692,6 +702,11 @@
 
     opaque_paint->p = p;
 
+    if ( root_transform == FT_COLOR_INCLUDE_ROOT_TRANSFORM )
+      opaque_paint->insert_root_transform = 1;
+    else
+      opaque_paint->insert_root_transform = 0;
+
     return 1;
   }
 
@@ -737,8 +752,12 @@
            colr->num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE ) )
       return 0;
 
-    paint_offset    = FT_NEXT_ULONG( p );
-    opaque_paint->p = (FT_Byte*)( colr->layers_v1 + paint_offset );
+    paint_offset =
+      FT_NEXT_ULONG( p );
+    opaque_paint->insert_root_transform =
+      0;
+    opaque_paint->p =
+      (FT_Byte*)( colr->layers_v1 + paint_offset );
 
     iterator->p = p;
 
@@ -794,21 +813,74 @@
                      FT_OpaquePaint  opaque_paint,
                      FT_COLR_Paint*  paint )
   {
-    Colr*  colr = (Colr*)face->colr;
+    Colr*           colr = (Colr*)face->colr;
+    FT_OpaquePaint  next_paint;
+    FT_Matrix       ft_root_scale;
 
-    FT_Byte*  p;
+    if ( !colr || !colr->base_glyphs_v1 || !colr->table )
+      return 0;
 
+    if ( opaque_paint.insert_root_transform )
+    {
+      /* 'COLR' v1 glyph information is returned in unscaled coordinates,
+       * i.e., `FT_Size` is not applied or multiplied into the values.  When
+       * client applications draw color glyphs, they can request to include
+       * a top-level transform, which includes the active `x_scale` and
+       * `y_scale` information for scaling the glyph, as well the additional
+       * transform and translate configured through `FT_Set_Transform`.
+       * This allows client applications to apply this top-level transform
+       * to the graphics context first and only once, then have gradient and
+       * contour scaling applied correctly when performing the additional
+       * drawing operations for subsequenct paints.  Prepare this initial
+       * transform here.
+       */
+      paint->format = FT_COLR_PAINTFORMAT_TRANSFORMED;
 
-    if ( !colr )
-      return 0;
+      next_paint.p                     = opaque_paint.p;
+      next_paint.insert_root_transform = 0;
+      paint->u.transformed.paint       = next_paint;
 
-    if ( opaque_paint.p < (FT_Byte*)colr->table                         ||
-         opaque_paint.p >= ( (FT_Byte*)colr->table + colr->table_size ) )
-      return 0;
+      /* `x_scale` and `y_scale` are in 26.6 format, representing the scale
+       * factor to get from font units to requested size.  However, expected
+       * return values are in 16.16, so we shift accordingly with rounding. 
+       */
+      ft_root_scale.xx = ( face->root.size->metrics.x_scale + 32 ) >> 6;
+      ft_root_scale.xy = 0;
+      ft_root_scale.yx = 0;
+      ft_root_scale.yy = ( face->root.size->metrics.y_scale + 32 ) >> 6;
+
+      if ( face->root.internal->transform_flags & 1 )
+        FT_Matrix_Multiply( &face->root.internal->transform_matrix,
+                            &ft_root_scale );
+
+      paint->u.transformed.affine.xx = ft_root_scale.xx;
+      paint->u.transformed.affine.xy = ft_root_scale.xy;
+      paint->u.transformed.affine.yx = ft_root_scale.yx;
+      paint->u.transformed.affine.yy = ft_root_scale.yy;
+
+      /* The translation is specified in 26.6 format and, according to the
+       * documentation of `FT_Set_Translate`, is performed on the character
+       * size given in the last call to `FT_Set_Char_Size`.  The
+       * 'PaintTransformed' paint table's `FT_Affine23` format expects
+       * values in 16.16 format, thus we need to shift by 10 bits.
+       */
+      if ( face->root.internal->transform_flags & 2 )
+      {
+        paint->u.transformed.affine.dx =
+          face->root.internal->transform_delta.x << 10;
+        paint->u.transformed.affine.dy =
+          face->root.internal->transform_delta.y << 10;
+      }
+      else
+      {
+        paint->u.transformed.affine.dx = 0;
+        paint->u.transformed.affine.dy = 0;
+      }
 
-    p = opaque_paint.p;
+      return 1;
+    }
 
-    return read_paint( colr, p, paint );
+    return read_paint( colr, opaque_paint.p, paint );
   }
 
 
diff --git a/src/sfnt/ttcolr.h b/src/sfnt/ttcolr.h
index d9ba9ac..4a7ed4b 100644
--- a/src/sfnt/ttcolr.h
+++ b/src/sfnt/ttcolr.h
@@ -43,9 +43,10 @@ FT_BEGIN_HEADER
                           FT_LayerIterator*  iterator );
 
   FT_LOCAL( FT_Bool )
-  tt_face_get_colr_glyph_paint( TT_Face          face,
-                                FT_UInt          base_glyph,
-                                FT_OpaquePaint*  paint );
+  tt_face_get_colr_glyph_paint( TT_Face                  face,
+                                FT_UInt                  base_glyph,
+                                FT_Color_Root_Transform  root_transform,
+                                FT_OpaquePaint*          paint );
 
   FT_LOCAL ( FT_Bool )
   tt_face_get_paint_layers( TT_Face            face,