tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-ft-colr.hh (18916B)


      1 /*
      2 * Copyright © 2022  Behdad Esfahbod
      3 *
      4 *  This is part of HarfBuzz, a text shaping library.
      5 *
      6 * Permission is hereby granted, without written agreement and without
      7 * license or royalty fees, to use, copy, modify, and distribute this
      8 * software and its documentation for any purpose, provided that the
      9 * above copyright notice and the following two paragraphs appear in
     10 * all copies of this software.
     11 *
     12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16 * DAMAGE.
     17 *
     18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23 */
     24 
     25 #ifndef HB_FT_COLR_HH
     26 #define HB_FT_COLR_HH
     27 
     28 #include "hb.hh"
     29 
     30 #include "hb-decycler.hh"
     31 #include "hb-paint-bounded.hh"
     32 
     33 #include FT_COLOR_H
     34 
     35 
     36 static hb_paint_composite_mode_t
     37 _hb_ft_paint_composite_mode (FT_Composite_Mode mode)
     38 {
     39  switch (mode)
     40  {
     41    case FT_COLR_COMPOSITE_CLEAR:          return HB_PAINT_COMPOSITE_MODE_CLEAR;
     42    case FT_COLR_COMPOSITE_SRC:            return HB_PAINT_COMPOSITE_MODE_SRC;
     43    case FT_COLR_COMPOSITE_DEST:           return HB_PAINT_COMPOSITE_MODE_DEST;
     44    case FT_COLR_COMPOSITE_SRC_OVER:       return HB_PAINT_COMPOSITE_MODE_SRC_OVER;
     45    case FT_COLR_COMPOSITE_DEST_OVER:      return HB_PAINT_COMPOSITE_MODE_DEST_OVER;
     46    case FT_COLR_COMPOSITE_SRC_IN:         return HB_PAINT_COMPOSITE_MODE_SRC_IN;
     47    case FT_COLR_COMPOSITE_DEST_IN:        return HB_PAINT_COMPOSITE_MODE_DEST_IN;
     48    case FT_COLR_COMPOSITE_SRC_OUT:        return HB_PAINT_COMPOSITE_MODE_SRC_OUT;
     49    case FT_COLR_COMPOSITE_DEST_OUT:       return HB_PAINT_COMPOSITE_MODE_DEST_OUT;
     50    case FT_COLR_COMPOSITE_SRC_ATOP:       return HB_PAINT_COMPOSITE_MODE_SRC_ATOP;
     51    case FT_COLR_COMPOSITE_DEST_ATOP:      return HB_PAINT_COMPOSITE_MODE_DEST_ATOP;
     52    case FT_COLR_COMPOSITE_XOR:            return HB_PAINT_COMPOSITE_MODE_XOR;
     53    case FT_COLR_COMPOSITE_PLUS:           return HB_PAINT_COMPOSITE_MODE_PLUS;
     54    case FT_COLR_COMPOSITE_SCREEN:         return HB_PAINT_COMPOSITE_MODE_SCREEN;
     55    case FT_COLR_COMPOSITE_OVERLAY:        return HB_PAINT_COMPOSITE_MODE_OVERLAY;
     56    case FT_COLR_COMPOSITE_DARKEN:         return HB_PAINT_COMPOSITE_MODE_DARKEN;
     57    case FT_COLR_COMPOSITE_LIGHTEN:        return HB_PAINT_COMPOSITE_MODE_LIGHTEN;
     58    case FT_COLR_COMPOSITE_COLOR_DODGE:    return HB_PAINT_COMPOSITE_MODE_COLOR_DODGE;
     59    case FT_COLR_COMPOSITE_COLOR_BURN:     return HB_PAINT_COMPOSITE_MODE_COLOR_BURN;
     60    case FT_COLR_COMPOSITE_HARD_LIGHT:     return HB_PAINT_COMPOSITE_MODE_HARD_LIGHT;
     61    case FT_COLR_COMPOSITE_SOFT_LIGHT:     return HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT;
     62    case FT_COLR_COMPOSITE_DIFFERENCE:     return HB_PAINT_COMPOSITE_MODE_DIFFERENCE;
     63    case FT_COLR_COMPOSITE_EXCLUSION:      return HB_PAINT_COMPOSITE_MODE_EXCLUSION;
     64    case FT_COLR_COMPOSITE_MULTIPLY:       return HB_PAINT_COMPOSITE_MODE_MULTIPLY;
     65    case FT_COLR_COMPOSITE_HSL_HUE:        return HB_PAINT_COMPOSITE_MODE_HSL_HUE;
     66    case FT_COLR_COMPOSITE_HSL_SATURATION: return HB_PAINT_COMPOSITE_MODE_HSL_SATURATION;
     67    case FT_COLR_COMPOSITE_HSL_COLOR:      return HB_PAINT_COMPOSITE_MODE_HSL_COLOR;
     68    case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY;
     69 
     70    case FT_COLR_COMPOSITE_MAX:            HB_FALLTHROUGH;
     71    default:                               return HB_PAINT_COMPOSITE_MODE_CLEAR;
     72  }
     73 }
     74 
     75 typedef struct hb_ft_paint_context_t hb_ft_paint_context_t;
     76 
     77 static void
     78 _hb_ft_paint (hb_ft_paint_context_t *c,
     79       FT_OpaquePaint opaque_paint);
     80 
     81 struct hb_ft_paint_context_t
     82 {
     83  hb_ft_paint_context_t (const hb_ft_font_t *ft_font_,
     84 		 hb_font_t *font_,
     85 		 hb_paint_funcs_t *paint_funcs, void *paint_data,
     86 		 hb_array_t<const FT_Color> palette,
     87 		 unsigned palette_index,
     88 		 hb_color_t foreground) :
     89    ft_font (ft_font_), font (font_),
     90    funcs (paint_funcs), data (paint_data),
     91    palette (palette), palette_index (palette_index), foreground (foreground)
     92  {
     93    if (font->is_synthetic)
     94    {
     95      font = hb_font_create_sub_font (font);
     96      hb_font_set_synthetic_bold (font, 0, 0, true);
     97      hb_font_set_synthetic_slant (font, 0);
     98    }
     99    else
    100      hb_font_reference (font);
    101  }
    102 
    103  ~hb_ft_paint_context_t ()
    104  {
    105    hb_font_destroy (font);
    106  }
    107 
    108  void recurse (FT_OpaquePaint paint)
    109  {
    110    if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
    111    depth_left--;
    112    edge_count--;
    113    _hb_ft_paint (this, paint);
    114    depth_left++;
    115  }
    116 
    117  const hb_ft_font_t *ft_font;
    118  hb_font_t *font;
    119  hb_paint_funcs_t *funcs;
    120  void *data;
    121  hb_array_t<const FT_Color> palette;
    122  unsigned palette_index;
    123  hb_color_t foreground;
    124  hb_decycler_t glyphs_decycler;
    125  hb_decycler_t layers_decycler;
    126  int depth_left = HB_MAX_NESTING_LEVEL;
    127  int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
    128 };
    129 
    130 static unsigned
    131 _hb_ft_color_line_get_color_stops (hb_color_line_t *color_line,
    132 			   void *color_line_data,
    133 			   unsigned int start,
    134 			   unsigned int *count,
    135 			   hb_color_stop_t *color_stops,
    136 			   void *user_data)
    137 {
    138  FT_ColorLine *cl = (FT_ColorLine *) color_line_data;
    139  hb_ft_paint_context_t *c = (hb_ft_paint_context_t *) user_data;
    140 
    141  if (count)
    142  {
    143    FT_ColorStop stop;
    144    unsigned wrote = 0;
    145    FT_ColorStopIterator iter = cl->color_stop_iterator;
    146 
    147    if (start >= cl->color_stop_iterator.num_color_stops)
    148    {
    149      *count = 0;
    150      return cl->color_stop_iterator.num_color_stops;
    151    }
    152 
    153    while (cl->color_stop_iterator.current_color_stop < start)
    154      FT_Get_Colorline_Stops(c->ft_font->ft_face,
    155 		     &stop,
    156 		     &cl->color_stop_iterator);
    157 
    158    while (count && *count &&
    159    FT_Get_Colorline_Stops(c->ft_font->ft_face,
    160 			  &stop,
    161 			  &cl->color_stop_iterator))
    162    {
    163      // https://github.com/harfbuzz/harfbuzz/issues/4013
    164      if (sizeof stop.stop_offset == 2)
    165 color_stops->offset = stop.stop_offset / 16384.f;
    166      else
    167 color_stops->offset = stop.stop_offset / 65536.f;
    168 
    169      color_stops->is_foreground = stop.color.palette_index == 0xFFFF;
    170      if (color_stops->is_foreground)
    171 color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground),
    172 			       hb_color_get_green (c->foreground),
    173 			       hb_color_get_red (c->foreground),
    174 			       (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14);
    175      else
    176      {
    177 hb_color_t color;
    178        if (c->funcs->custom_palette_color (c->data, stop.color.palette_index, &color))
    179 {
    180   color_stops->color = HB_COLOR (hb_color_get_blue (color),
    181 				 hb_color_get_green (color),
    182 				 hb_color_get_red (color),
    183 				 (hb_color_get_alpha (color) * stop.color.alpha) >> 14);
    184 }
    185 else if (c->palette)
    186 {
    187   FT_Color ft_color = c->palette[stop.color.palette_index];
    188   color_stops->color = HB_COLOR (ft_color.blue,
    189 				 ft_color.green,
    190 				 ft_color.red,
    191 				 (ft_color.alpha * stop.color.alpha) >> 14);
    192 }
    193 else
    194   color_stops->color = HB_COLOR (0, 0, 0, 0);
    195      }
    196 
    197      color_stops++;
    198      wrote++;
    199    }
    200 
    201    *count = wrote;
    202 
    203    // reset the iterator for next time
    204    cl->color_stop_iterator = iter;
    205  }
    206 
    207  return cl->color_stop_iterator.num_color_stops;
    208 }
    209 
    210 static hb_paint_extend_t
    211 _hb_ft_color_line_get_extend (hb_color_line_t *color_line,
    212 		      void *color_line_data,
    213 		      void *user_data)
    214 {
    215  FT_ColorLine *c = (FT_ColorLine *) color_line_data;
    216  switch (c->extend)
    217  {
    218    default:
    219    case FT_COLR_PAINT_EXTEND_PAD:     return HB_PAINT_EXTEND_PAD;
    220    case FT_COLR_PAINT_EXTEND_REPEAT:  return HB_PAINT_EXTEND_REPEAT;
    221    case FT_COLR_PAINT_EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT;
    222  }
    223 }
    224 
    225 void
    226 _hb_ft_paint (hb_ft_paint_context_t *c,
    227       FT_OpaquePaint opaque_paint)
    228 {
    229  FT_Face ft_face = c->ft_font->ft_face;
    230  FT_COLR_Paint paint;
    231  if (!FT_Get_Paint (ft_face, opaque_paint, &paint))
    232    return;
    233 
    234  switch (paint.format)
    235  {
    236    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
    237    {
    238      FT_OpaquePaint other_paint = {0};
    239      hb_decycler_node_t node (c->layers_decycler);
    240      while (FT_Get_Paint_Layers (ft_face,
    241 			  &paint.u.colr_layers.layer_iterator,
    242 			  &other_paint))
    243      {
    244 // FreeType doesn't provide a way to get the layer index, so we use the pointer
    245 // for cycle detection.
    246 if (unlikely (!node.visit ((uintptr_t) other_paint.p)))
    247   continue;
    248 
    249 c->recurse (other_paint);
    250      }
    251    }
    252    break;
    253    case FT_COLR_PAINTFORMAT_SOLID:
    254    {
    255      bool is_foreground = paint.u.solid.color.palette_index ==  0xFFFF;
    256      hb_color_t color;
    257      if (is_foreground)
    258 color = HB_COLOR (hb_color_get_blue (c->foreground),
    259 		  hb_color_get_green (c->foreground),
    260 		  hb_color_get_red (c->foreground),
    261 		  (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14);
    262      else
    263      {
    264 if (c->funcs->custom_palette_color (c->data, paint.u.solid.color.palette_index, &color))
    265 {
    266   color = HB_COLOR (hb_color_get_blue (color),
    267 		    hb_color_get_green (color),
    268 		    hb_color_get_red (color),
    269 		    (hb_color_get_alpha (color) * paint.u.solid.color.alpha) >> 14);
    270 }
    271 else
    272 {
    273   FT_Color ft_color = c->palette[paint.u.solid.color.palette_index];
    274   color = HB_COLOR (ft_color.blue,
    275 		    ft_color.green,
    276 		    ft_color.red,
    277 		    (ft_color.alpha * paint.u.solid.color.alpha) >> 14);
    278 }
    279      }
    280      c->funcs->color (c->data, is_foreground, color);
    281    }
    282    break;
    283    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
    284    {
    285      hb_color_line_t cl = {
    286 &paint.u.linear_gradient.colorline,
    287 _hb_ft_color_line_get_color_stops, c,
    288 _hb_ft_color_line_get_extend, nullptr
    289      };
    290 
    291      c->funcs->linear_gradient (c->data, &cl,
    292 			 paint.u.linear_gradient.p0.x / 65536.f,
    293 			 paint.u.linear_gradient.p0.y / 65536.f,
    294 			 paint.u.linear_gradient.p1.x / 65536.f,
    295 			 paint.u.linear_gradient.p1.y / 65536.f,
    296 			 paint.u.linear_gradient.p2.x / 65536.f,
    297 			 paint.u.linear_gradient.p2.y / 65536.f);
    298    }
    299    break;
    300    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
    301    {
    302      hb_color_line_t cl = {
    303 &paint.u.linear_gradient.colorline,
    304 _hb_ft_color_line_get_color_stops, c,
    305 _hb_ft_color_line_get_extend, nullptr
    306      };
    307 
    308      c->funcs->radial_gradient (c->data, &cl,
    309 			 paint.u.radial_gradient.c0.x / 65536.f,
    310 			 paint.u.radial_gradient.c0.y / 65536.f,
    311 			 paint.u.radial_gradient.r0 / 65536.f,
    312 			 paint.u.radial_gradient.c1.x / 65536.f,
    313 			 paint.u.radial_gradient.c1.y / 65536.f,
    314 			 paint.u.radial_gradient.r1 / 65536.f);
    315    }
    316    break;
    317    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
    318    {
    319      hb_color_line_t cl = {
    320 &paint.u.linear_gradient.colorline,
    321 _hb_ft_color_line_get_color_stops, c,
    322 _hb_ft_color_line_get_extend, nullptr
    323      };
    324 
    325      c->funcs->sweep_gradient (c->data, &cl,
    326 			paint.u.sweep_gradient.center.x / 65536.f,
    327 			paint.u.sweep_gradient.center.y / 65536.f,
    328 			(paint.u.sweep_gradient.start_angle / 65536.f + 1) * HB_PI,
    329 			(paint.u.sweep_gradient.end_angle / 65536.f + 1) * HB_PI);
    330    }
    331    break;
    332    case FT_COLR_PAINTFORMAT_GLYPH:
    333    {
    334      c->funcs->push_inverse_font_transform (c->data, c->font);
    335      c->ft_font->lock.unlock ();
    336      c->funcs->push_clip_glyph (c->data, paint.u.glyph.glyphID, c->font);
    337      c->ft_font->lock.lock ();
    338      c->funcs->push_font_transform (c->data, c->font);
    339      c->recurse (paint.u.glyph.paint);
    340      c->funcs->pop_transform (c->data);
    341      c->funcs->pop_clip (c->data);
    342      c->funcs->pop_transform (c->data);
    343    }
    344    break;
    345    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
    346    {
    347      hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
    348 
    349      hb_decycler_node_t node (c->glyphs_decycler);
    350      if (unlikely (!node.visit (gid)))
    351 return;
    352 
    353      c->funcs->push_inverse_font_transform (c->data, c->font);
    354      c->ft_font->lock.unlock ();
    355      if (c->funcs->color_glyph (c->data, gid, c->font))
    356      {
    357 c->ft_font->lock.lock ();
    358 c->funcs->pop_transform (c->data);
    359 return;
    360      }
    361      c->ft_font->lock.lock ();
    362      c->funcs->pop_transform (c->data);
    363 
    364      FT_OpaquePaint other_paint = {0};
    365      if (FT_Get_Color_Glyph_Paint (ft_face, gid,
    366 			    FT_COLOR_NO_ROOT_TRANSFORM,
    367 			    &other_paint))
    368      {
    369        bool has_clip_box;
    370        FT_ClipBox clip_box;
    371        has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box);
    372 
    373        if (has_clip_box)
    374 {
    375   /* The FreeType ClipBox is in scaled coordinates, whereas we need
    376    * unscaled clipbox here. Oh well...
    377    */
    378 
    379   float upem = c->font->face->get_upem ();
    380   float xscale = upem / (c->font->x_scale ? c->font->x_scale : upem);
    381   float yscale = upem / (c->font->y_scale ? c->font->y_scale : upem);
    382 
    383          c->funcs->push_clip_rectangle (c->data,
    384 				 clip_box.bottom_left.x * xscale,
    385 				 clip_box.bottom_left.y * yscale,
    386 				 clip_box.top_right.x * xscale,
    387 				 clip_box.top_right.y * yscale);
    388 }
    389 
    390 c->recurse (other_paint);
    391 
    392        if (has_clip_box)
    393          c->funcs->pop_clip (c->data);
    394      }
    395    }
    396    break;
    397    case FT_COLR_PAINTFORMAT_TRANSFORM:
    398    {
    399      c->funcs->push_transform (c->data,
    400 			paint.u.transform.affine.xx / 65536.f,
    401 			paint.u.transform.affine.yx / 65536.f,
    402 			paint.u.transform.affine.xy / 65536.f,
    403 			paint.u.transform.affine.yy / 65536.f,
    404 			paint.u.transform.affine.dx / 65536.f,
    405 			paint.u.transform.affine.dy / 65536.f);
    406      c->recurse (paint.u.transform.paint);
    407      c->funcs->pop_transform (c->data);
    408    }
    409    break;
    410    case FT_COLR_PAINTFORMAT_TRANSLATE:
    411    {
    412      float dx = paint.u.translate.dx / 65536.f;
    413      float dy = paint.u.translate.dy / 65536.f;
    414 
    415      c->funcs->push_translate (c->data, dx, dy);
    416      c->recurse (paint.u.translate.paint);
    417      c->funcs->pop_transform (c->data);
    418    }
    419    break;
    420    case FT_COLR_PAINTFORMAT_SCALE:
    421    {
    422      float dx = paint.u.scale.center_x / 65536.f;
    423      float dy = paint.u.scale.center_y / 65536.f;
    424      float sx = paint.u.scale.scale_x / 65536.f;
    425      float sy = paint.u.scale.scale_y / 65536.f;
    426 
    427      c->funcs->push_scale_around_center (c->data, sx, sy, dx, dy);
    428      c->recurse (paint.u.scale.paint);
    429      c->funcs->pop_transform (c->data);
    430    }
    431    break;
    432    case FT_COLR_PAINTFORMAT_ROTATE:
    433    {
    434      float dx = paint.u.rotate.center_x / 65536.f;
    435      float dy = paint.u.rotate.center_y / 65536.f;
    436      float a = paint.u.rotate.angle / 65536.f;
    437 
    438      c->funcs->push_rotate_around_center (c->data, a, dx, dy);
    439      c->recurse (paint.u.rotate.paint);
    440      c->funcs->pop_transform (c->data);
    441    }
    442    break;
    443    case FT_COLR_PAINTFORMAT_SKEW:
    444    {
    445      float dx = paint.u.skew.center_x / 65536.f;
    446      float dy = paint.u.skew.center_y / 65536.f;
    447      float sx = paint.u.skew.x_skew_angle / 65536.f;
    448      float sy = paint.u.skew.y_skew_angle / 65536.f;
    449 
    450      c->funcs->push_skew_around_center (c->data, sx, sy, dx, dy);
    451      c->recurse (paint.u.skew.paint);
    452      c->funcs->pop_transform (c->data);
    453    }
    454    break;
    455    case FT_COLR_PAINTFORMAT_COMPOSITE:
    456    {
    457      c->funcs->push_group (c->data);
    458      c->recurse (paint.u.composite.backdrop_paint);
    459      c->funcs->push_group (c->data);
    460      c->recurse (paint.u.composite.source_paint);
    461      c->funcs->pop_group (c->data, _hb_ft_paint_composite_mode (paint.u.composite.composite_mode));
    462      c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
    463    }
    464    break;
    465 
    466    case FT_COLR_PAINT_FORMAT_MAX: break;
    467    default: HB_FALLTHROUGH;
    468    case FT_COLR_PAINTFORMAT_UNSUPPORTED: break;
    469  }
    470 }
    471 
    472 
    473 static bool
    474 hb_ft_paint_glyph_colr (hb_font_t *font,
    475 		void *font_data,
    476 		hb_codepoint_t gid,
    477 		hb_paint_funcs_t *paint_funcs, void *paint_data,
    478 		unsigned int palette_index,
    479 		hb_color_t foreground,
    480 		void *user_data)
    481 {
    482  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
    483  FT_Face ft_face = ft_font->ft_face;
    484 
    485  /* Face is locked. */
    486 
    487  FT_Palette_Data   palette_data = {};
    488  FT_Color*         palette = NULL;
    489  FT_LayerIterator  iterator;
    490 
    491  FT_Bool  have_layers;
    492  FT_UInt  layer_glyph_index;
    493  FT_UInt  layer_color_index;
    494 
    495  (void) FT_Palette_Data_Get(ft_face, &palette_data);
    496  (void) FT_Palette_Select(ft_face, palette_index, &palette);
    497  if (!palette)
    498  {
    499    // https://github.com/harfbuzz/harfbuzz/issues/5116
    500    (void) FT_Palette_Select(ft_face, 0, &palette);
    501  }
    502 
    503  auto palette_array = hb_array ((const FT_Color *) palette,
    504 			 palette ? palette_data.num_palette_entries : 0);
    505 
    506  /* COLRv1 */
    507  FT_OpaquePaint paint = {0};
    508  if (FT_Get_Color_Glyph_Paint (ft_face, gid,
    509 		        FT_COLOR_NO_ROOT_TRANSFORM,
    510 		        &paint))
    511  {
    512    hb_ft_paint_context_t c (ft_font, font,
    513 		     paint_funcs, paint_data,
    514 		     palette_array, palette_index, foreground);
    515    hb_decycler_node_t node (c.glyphs_decycler);
    516    node.visit (gid);
    517 
    518    bool clip = false;
    519    bool is_bounded = false;
    520    FT_ClipBox clip_box;
    521    if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box))
    522    {
    523      c.funcs->push_clip_rectangle (c.data,
    524 			    clip_box.bottom_left.x,
    525 			    clip_box.bottom_left.y,
    526 			    clip_box.top_right.x,
    527 			    clip_box.top_right.y);
    528      clip = true;
    529      is_bounded = true;
    530    }
    531    if (!is_bounded)
    532    {
    533      auto *bounded_funcs = hb_paint_bounded_get_funcs ();
    534      hb_paint_bounded_context_t bounded_data;
    535      hb_ft_paint_context_t ce (ft_font, font,
    536 		        bounded_funcs, &bounded_data,
    537 		        palette_array, palette_index, foreground);
    538      hb_decycler_node_t node2 (ce.glyphs_decycler);
    539      node2.visit (gid);
    540      ce.recurse (paint);
    541      is_bounded = bounded_data.is_bounded ();
    542    }
    543 
    544    c.funcs->push_font_transform (c.data, font);
    545 
    546    if (is_bounded)
    547      c.recurse (paint);
    548 
    549    c.funcs->pop_transform (c.data);
    550 
    551    if (clip)
    552      c.funcs->pop_clip (c.data);
    553 
    554    return true;
    555  }
    556 
    557  /* COLRv0 */
    558  iterator.p  = NULL;
    559  have_layers = FT_Get_Color_Glyph_Layer(ft_face,
    560 				 gid,
    561 				 &layer_glyph_index,
    562 				 &layer_color_index,
    563 				 &iterator);
    564 
    565  if (palette && have_layers)
    566  {
    567    do
    568    {
    569      hb_bool_t is_foreground = true;
    570      hb_color_t color = foreground;
    571 
    572      if ( layer_color_index != 0xFFFF )
    573      {
    574 FT_Color layer_color = palette[layer_color_index];
    575 color = HB_COLOR (layer_color.blue,
    576 		  layer_color.green,
    577 		  layer_color.red,
    578 		  layer_color.alpha);
    579 is_foreground = false;
    580      }
    581 
    582      ft_font->lock.unlock ();
    583      paint_funcs->push_clip_glyph (paint_data, layer_glyph_index, font);
    584      ft_font->lock.lock ();
    585      paint_funcs->color (paint_data, is_foreground, color);
    586      paint_funcs->pop_clip (paint_data);
    587 
    588    } while (FT_Get_Color_Glyph_Layer(ft_face,
    589 			      gid,
    590 			      &layer_glyph_index,
    591 			      &layer_color_index,
    592 			      &iterator));
    593    return true;
    594  }
    595 
    596  return false;
    597 }
    598 
    599 
    600 #endif /* HB_FT_COLR_HH */