Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/harfbuzz/src/OT/Color/COLR/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 86 kB image not shown  

Quelle  COLR.hh

  Sprache: C
 

/*
 * Copyright © 2018  Ebrahim Byagowi
 * Copyright © 2020  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Calder Kitagawa
 */


#ifndef OT_COLOR_COLR_COLR_HH
#define OT_COLOR_COLR_COLR_HH

#include "../../../hb.hh"
#include "../../../hb-open-type.hh"
#include "../../../hb-ot-var-common.hh"
#include "../../../hb-paint.hh"
#include "../../../hb-paint-extents.hh"

/*
 * COLR -- Color
 * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
 */

#define HB_OT_TAG_COLR HB_TAG('C','O','L','R')

namespace OT {
struct hb_paint_context_t;
}

namespace OT {

struct COLR;

struct Paint;

struct hb_paint_context_t :
       hb_dispatch_context_t<hb_paint_context_t>
{
  const char *get_name () { return "PAINT"; }
  template <typename T>
  return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); }
  static return_t default_return_value () { return hb_empty_t (); }

  const COLR* get_colr_table () const
  { return reinterpret_cast<const COLR *> (base); }

public:
  const void *base;
  hb_paint_funcs_t *funcs;
  void *data;
  hb_font_t *font;
  unsigned int palette_index;
  hb_color_t foreground;
  ItemVarStoreInstancer &instancer;
  hb_map_t current_glyphs;
  hb_map_t current_layers;
  int depth_left = HB_MAX_NESTING_LEVEL;
  int edge_count = HB_MAX_GRAPH_EDGE_COUNT;

  hb_paint_context_t (const void *base_,
        hb_paint_funcs_t *funcs_,
        void *data_,
                      hb_font_t *font_,
                      unsigned int palette_,
                      hb_color_t foreground_,
        ItemVarStoreInstancer &instancer_) :
    base (base_),
    funcs (funcs_),
    data (data_),
    font (font_),
    palette_index (palette_),
    foreground (foreground_),
    instancer (instancer_)
  { }

  hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
  {
    hb_color_t color = foreground;

    *is_foreground = true;

    if (color_index != 0xffff)
    {
      if (!funcs->custom_palette_color (data, color_index, &color))
      {
 unsigned int clen = 1;
 hb_face_t *face = hb_font_get_face (font);

 hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
      }

      *is_foreground = false;
    }

    return HB_COLOR (hb_color_get_blue (color),
                     hb_color_get_green (color),
                     hb_color_get_red (color),
                     hb_color_get_alpha (color) * alpha);
  }

  inline void recurse (const Paint &paint);
};

struct hb_colrv1_closure_context_t :
       hb_dispatch_context_t<hb_colrv1_closure_context_t>
{
  template <typename T>
  return_t dispatch (const T &obj)
  {
    if (unlikely (nesting_level_left == 0))
      return hb_empty_t ();

    if (paint_visited (&obj))
      return hb_empty_t ();

    nesting_level_left--;
    obj.closurev1 (this);
    nesting_level_left++;
    return hb_empty_t ();
  }
  static return_t default_return_value () { return hb_empty_t (); }

  bool paint_visited (const void *paint)
  {
    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base);
    if (visited_paint.in_error() || visited_paint.has (delta))
      return true;

    visited_paint.add (delta);
    return false;
  }

  const COLR* get_colr_table () const
  { return reinterpret_cast<const COLR *> (base); }

  void add_glyph (unsigned glyph_id)
  { glyphs->add (glyph_id); }

  void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers)
  { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); }

  void add_palette_index (unsigned palette_index)
  { palette_indices->add (palette_index); }

  void add_var_idxes (unsigned first_var_idx, unsigned num_idxes)
  {
    if (!num_idxes || first_var_idx == VarIdx::NO_VARIATION) return;
    variation_indices->add_range (first_var_idx, first_var_idx + num_idxes - 1);
  }

  public:
  const void *base;
  hb_set_t visited_paint;
  hb_set_t *glyphs;
  hb_set_t *layer_indices;
  hb_set_t *palette_indices;
  hb_set_t *variation_indices;
  unsigned num_var_idxes;
  unsigned nesting_level_left;

  hb_colrv1_closure_context_t (const void *base_,
                               hb_set_t *glyphs_,
                               hb_set_t *layer_indices_,
                               hb_set_t *palette_indices_,
                               hb_set_t *variation_indices_,
                               unsigned num_var_idxes_ = 1,
                               unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
                          base (base_),
                          glyphs (glyphs_),
                          layer_indices (layer_indices_),
                          palette_indices (palette_indices_),
                          variation_indices (variation_indices_),
                          num_var_idxes (num_var_idxes_),
                          nesting_level_left (nesting_level_left_)
  {}
};

struct LayerRecord
{
  operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  public:
  HBGlyphID16 glyphId; /* Glyph ID of layer glyph */
  Index  colorIdx; /* Index value to use with a
 * selected color palette.
 * An index value of 0xFFFF
 * is a special case indicating
 * that the text foreground
 * color (defined by a
 * higher-level client) should
 * be used and shall not be
 * treated as actual index
 * into CPAL ColorRecord array. */

  public:
  DEFINE_SIZE_STATIC (4);
};

struct BaseGlyphRecord
{
  int cmp (hb_codepoint_t g) const
  { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  public:
  HBGlyphID16 glyphId; /* Glyph ID of reference glyph */
  HBUINT16 firstLayerIdx; /* Index (from beginning of
 * the Layer Records) to the
 * layer record. There will be
 * numLayers consecutive entries
 * for this base glyph. */

  HBUINT16 numLayers; /* Number of color layers
 * associated with this glyph */

  public:
  DEFINE_SIZE_STATIC (6);
};

template <typename T>
struct Variable
{
  static constexpr bool is_variable = true;

  Variable<T>* copy (hb_serialize_context_t *c) const
  {
    TRACE_SERIALIZE (this);
    return_trace (c->embed (this));
  }

  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    c->num_var_idxes = 0;
    // update c->num_var_idxes during value closure
    value.closurev1 (c);
    c->add_var_idxes (varIdxBase, c->num_var_idxes);
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
    if (c->plan->all_axes_pinned)
      return_trace (true);

    VarIdx new_varidx;
    new_varidx = varIdxBase;
    if (varIdxBase != VarIdx::NO_VARIATION)
    {
      hb_pair_t<unsignedint> *new_varidx_delta;
      if (!c->plan->colrv1_variation_idx_delta_map.has (varIdxBase, &new_varidx_delta))
        return_trace (false);

      new_varidx = hb_first (*new_varidx_delta);
    }

    return_trace (c->serializer->embed (new_varidx));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && value.sanitize (c));
  }

  void paint_glyph (hb_paint_context_t *c) const
  {
    TRACE_PAINT (this);
    value.paint_glyph (c, varIdxBase);
  }

  void get_color_stop (hb_paint_context_t *c,
                       hb_color_stop_t *stop,
         const ItemVarStoreInstancer &instancer) const
  {
    value.get_color_stop (c, stop, varIdxBase, instancer);
  }

  hb_paint_extend_t get_extend () const
  {
    return value.get_extend ();
  }

  protected:
  T      value;
  public:
  VarIdx varIdxBase;
  public:
  DEFINE_SIZE_MIN (VarIdx::static_size + T::min_size);
};

template <typename T>
struct NoVariable
{
  static constexpr bool is_variable = false;

  static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION;

  NoVariable<T>* copy (hb_serialize_context_t *c) const
  {
    TRACE_SERIALIZE (this);
    return_trace (c->embed (this));
  }

  void closurev1 (hb_colrv1_closure_context_t* c) const
  { value.closurev1 (c); }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    return_trace (value.subset (c, instancer, varIdxBase));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && value.sanitize (c));
  }

  void paint_glyph (hb_paint_context_t *c) const
  {
    TRACE_PAINT (this);
    value.paint_glyph (c, varIdxBase);
  }

  void get_color_stop (hb_paint_context_t *c,
                       hb_color_stop_t *stop,
         const ItemVarStoreInstancer &instancer) const
  {
    value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
  }

  hb_paint_extend_t get_extend () const
  {
    return value.get_extend ();
  }

  T      value;
  public:
  DEFINE_SIZE_MIN (T::min_size);
};

// Color structures

struct ColorStop
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    c->add_palette_index (paletteIndex);
    c->num_var_idxes = 2;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (*this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0)));
      out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1)));
    }

    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  void get_color_stop (hb_paint_context_t *c,
                       hb_color_stop_t *out,
         uint32_t varIdx,
         const ItemVarStoreInstancer &instancer) const
  {
    out->offset = stopOffset.to_float(instancer (varIdx, 0));
    out->color = c->get_color (paletteIndex,
                               alpha.to_float (instancer (varIdx, 1)),
                               &out->is_foreground);
  }

  F2DOT14 stopOffset;
  HBUINT16 paletteIndex;
  F2DOT14 alpha;
  public:
  DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size);
};

struct Extend : HBUINT8
{
  enum {
    EXTEND_PAD     = 0,
    EXTEND_REPEAT  = 1,
    EXTEND_REFLECT = 2,
  };
  public:
  DEFINE_SIZE_STATIC (1);
};

template <template<typenameclass Var>
struct ColorLine
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    for (const auto &stop : stops.iter ())
      stop.closurev1 (c);
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->start_embed (this);
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);

    if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
    if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false);

    for (const auto& stop : stops.iter ())
    {
      if (!stop.subset (c, instancer)) return_trace (false);
    }
    return_trace (true);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  stops.sanitize (c));
  }

  /* get up to count stops from start */
  unsigned int
  get_color_stops (hb_paint_context_t *c,
                   unsigned int start,
     unsigned int *count,
     hb_color_stop_t *color_stops,
     const ItemVarStoreInstancer &instancer) const
  {
    unsigned int len = stops.len;

    if (count && color_stops)
    {
      unsigned int i;
      for (i = 0; i < *count && start + i < len; i++)
        stops[start + i].get_color_stop (c, &color_stops[i], instancer);
      *count = i;
    }

    return len;
  }

  HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line,
         void *color_line_data,
         unsigned int start,
         unsigned int *count,
         hb_color_stop_t *color_stops,
         void *user_data)
  {
    const ColorLine *thiz = (const ColorLine *) color_line_data;
    hb_paint_context_t *c = (hb_paint_context_t *) user_data;
    return thiz->get_color_stops (c, start, count, color_stops, c->instancer);
  }

  hb_paint_extend_t get_extend () const
  {
    return (hb_paint_extend_t) (unsigned int) extend;
  }

  HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line,
         void *color_line_data,
         void *user_data)
  {
    const ColorLine *thiz = (const ColorLine *) color_line_data;
    return thiz->get_extend ();
  }

  Extend extend;
  Array16Of<Var<ColorStop>> stops;
  public:
  DEFINE_SIZE_ARRAY_SIZED (3, stops);
};

// Composition modes

// Compositing modes are taken from https://www.w3.org/TR/compositing-1/
// NOTE: a brief audit of major implementations suggests most support most
// or all of the specified modes.
struct CompositeMode : HBUINT8
{
  enum {
    // Porter-Duff modes
    // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators
    COMPOSITE_CLEAR          =  0,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear
    COMPOSITE_SRC            =  1,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src
    COMPOSITE_DEST           =  2,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst
    COMPOSITE_SRC_OVER       =  3,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover
    COMPOSITE_DEST_OVER      =  4,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover
    COMPOSITE_SRC_IN         =  5,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin
    COMPOSITE_DEST_IN        =  6,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin
    COMPOSITE_SRC_OUT        =  7,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout
    COMPOSITE_DEST_OUT       =  8,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout
    COMPOSITE_SRC_ATOP       =  9,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop
    COMPOSITE_DEST_ATOP      = 10,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop
    COMPOSITE_XOR            = 11,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor
    COMPOSITE_PLUS           = 12,  // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus

    // Blend modes
    // https://www.w3.org/TR/compositing-1/#blending
    COMPOSITE_SCREEN         = 13,  // https://www.w3.org/TR/compositing-1/#blendingscreen
    COMPOSITE_OVERLAY        = 14,  // https://www.w3.org/TR/compositing-1/#blendingoverlay
    COMPOSITE_DARKEN         = 15,  // https://www.w3.org/TR/compositing-1/#blendingdarken
    COMPOSITE_LIGHTEN        = 16,  // https://www.w3.org/TR/compositing-1/#blendinglighten
    COMPOSITE_COLOR_DODGE    = 17,  // https://www.w3.org/TR/compositing-1/#blendingcolordodge
    COMPOSITE_COLOR_BURN     = 18,  // https://www.w3.org/TR/compositing-1/#blendingcolorburn
    COMPOSITE_HARD_LIGHT     = 19,  // https://www.w3.org/TR/compositing-1/#blendinghardlight
    COMPOSITE_SOFT_LIGHT     = 20,  // https://www.w3.org/TR/compositing-1/#blendingsoftlight
    COMPOSITE_DIFFERENCE     = 21,  // https://www.w3.org/TR/compositing-1/#blendingdifference
    COMPOSITE_EXCLUSION      = 22,  // https://www.w3.org/TR/compositing-1/#blendingexclusion
    COMPOSITE_MULTIPLY       = 23,  // https://www.w3.org/TR/compositing-1/#blendingmultiply

    // Modes that, uniquely, do not operate on components
    // https://www.w3.org/TR/compositing-1/#blendingnonseparable
    COMPOSITE_HSL_HUE        = 24,  // https://www.w3.org/TR/compositing-1/#blendinghue
    COMPOSITE_HSL_SATURATION = 25,  // https://www.w3.org/TR/compositing-1/#blendingsaturation
    COMPOSITE_HSL_COLOR      = 26,  // https://www.w3.org/TR/compositing-1/#blendingcolor
    COMPOSITE_HSL_LUMINOSITY = 27,  // https://www.w3.org/TR/compositing-1/#blendingluminosity
  };
  public:
  DEFINE_SIZE_STATIC (1);
};

struct Affine2x3
{
  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  void closurev1 (hb_colrv1_closure_context_t* c) const
  { c->num_var_idxes = 6; }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (*this);
    if (unlikely (!out)) return_trace (false);
    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->xx.set_float (xx.to_float(instancer (varIdxBase, 0)));
      out->yx.set_float (yx.to_float(instancer (varIdxBase, 1)));
      out->xy.set_float (xy.to_float(instancer (varIdxBase, 2)));
      out->yy.set_float (yy.to_float(instancer (varIdxBase, 3)));
      out->dx.set_float (dx.to_float(instancer (varIdxBase, 4)));
      out->dy.set_float (dy.to_float(instancer (varIdxBase, 5)));
    }
    return_trace (true);
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    c->funcs->push_transform (c->data,
         xx.to_float (c->instancer (varIdxBase, 0)),
         yx.to_float (c->instancer (varIdxBase, 1)),
                              xy.to_float (c->instancer (varIdxBase, 2)),
         yy.to_float (c->instancer (varIdxBase, 3)),
                              dx.to_float (c->instancer (varIdxBase, 4)),
         dy.to_float (c->instancer (varIdxBase, 5)));
  }

  F16DOT16 xx;
  F16DOT16 yx;
  F16DOT16 xy;
  F16DOT16 yy;
  F16DOT16 dx;
  F16DOT16 dy;
  public:
  DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
};

struct PaintColrLayers
{
  void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer HB_UNUSED) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);
    return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex),
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));

    return_trace (true);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  inline void paint_glyph (hb_paint_context_t *c) const;

  HBUINT8 format; /* format = 1 */
  HBUINT8 numLayers;
  HBUINT32 firstLayerIndex;  /* index into COLRv1::layerList */
  public:
  DEFINE_SIZE_STATIC (6);
};

struct PaintSolid
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    c->add_palette_index (paletteIndex);
    c->num_var_idxes = 1;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (*this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
      out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0)));

    if (format == 3 && c->plan->all_axes_pinned)
        out->format = 2;

    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    hb_bool_t is_foreground;
    hb_color_t color;

    color = c->get_color (paletteIndex,
                          alpha.to_float (c->instancer (varIdxBase, 0)),
                          &is_foreground);
    c->funcs->color (c->data, is_foreground, color);
  }

  HBUINT8 format; /* format = 2(noVar) or 3(Var)*/
  HBUINT16 paletteIndex;
  F2DOT14 alpha;
  public:
  DEFINE_SIZE_STATIC (3 + F2DOT14::static_size);
};

template <template<typenameclass Var>
struct PaintLinearGradient
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    (this+colorLine).closurev1 (c);
    c->num_var_idxes = 6;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
      out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
      out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2));
      out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3));
      out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4));
      out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5));
    }

    if (format == 5 && c->plan->all_axes_pinned)
        out->format = 4;

    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    hb_color_line_t cl = {
      (void *) &(this+colorLine),
      (this+colorLine).static_get_color_stops, c,
      (this+colorLine).static_get_extend, nullptr
    };

    c->funcs->linear_gradient (c->data, &cl,
          x0 + c->instancer (varIdxBase, 0),
          y0 + c->instancer (varIdxBase, 1),
          x1 + c->instancer (varIdxBase, 2),
          y1 + c->instancer (varIdxBase, 3),
          x2 + c->instancer (varIdxBase, 4),
          y2 + c->instancer (varIdxBase, 5));
  }

  HBUINT8   format; /* format = 4(noVar) or 5 (Var) */
  Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintLinearGradient
                                            * table) to ColorLine subtable. */

  FWORD   x0;
  FWORD   y0;
  FWORD   x1;
  FWORD   y1;
  FWORD   x2;
  FWORD   y2;
  public:
  DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
};

template <template<typenameclass Var>
struct PaintRadialGradient
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    (this+colorLine).closurev1 (c);
    c->num_var_idxes = 6;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
      out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
      out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2));
      out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3));
      out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4));
      out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5));
    }

    if (format == 7 && c->plan->all_axes_pinned)
        out->format = 6;

    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    hb_color_line_t cl = {
      (void *) &(this+colorLine),
      (this+colorLine).static_get_color_stops, c,
      (this+colorLine).static_get_extend, nullptr
    };

    c->funcs->radial_gradient (c->data, &cl,
          x0 + c->instancer (varIdxBase, 0),
          y0 + c->instancer (varIdxBase, 1),
          radius0 + c->instancer (varIdxBase, 2),
          x1 + c->instancer (varIdxBase, 3),
          y1 + c->instancer (varIdxBase, 4),
          radius1 + c->instancer (varIdxBase, 5));
  }

  HBUINT8   format; /* format = 6(noVar) or 7 (Var) */
  Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintRadialGradient
                                            * table) to ColorLine subtable. */

  FWORD   x0;
  FWORD   y0;
  UFWORD  radius0;
  FWORD   x1;
  FWORD   y1;
  UFWORD  radius1;
  public:
  DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
};

template <template<typenameclass Var>
struct PaintSweepGradient
{
  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    (this+colorLine).closurev1 (c);
    c->num_var_idxes = 4;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0));
      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1));
      out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2)));
      out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3)));
    }

    if (format == 9 && c->plan->all_axes_pinned)
        out->format = 8;

    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    hb_color_line_t cl = {
      (void *) &(this+colorLine),
      (this+colorLine).static_get_color_stops, c,
      (this+colorLine).static_get_extend, nullptr
    };

    c->funcs->sweep_gradient (c->data, &cl,
         centerX + c->instancer (varIdxBase, 0),
         centerY + c->instancer (varIdxBase, 1),
                              (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI,
                              (endAngle.to_float   (c->instancer (varIdxBase, 3)) + 1) * HB_PI);
  }

  HBUINT8   format; /* format = 8(noVar) or 9 (Var) */
  Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintSweepGradient
                                            * table) to ColorLine subtable. */

  FWORD   centerX;
  FWORD   centerY;
  F2DOT14  startAngle;
  F2DOT14  endAngle;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size);
};

// Paint a non-COLR glyph, filled as indicated by paint.
struct PaintGlyph
{
  void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
                                       HB_SERIALIZE_ERROR_INT_OVERFLOW))
      return_trace (false);

    return_trace (out->paint.serialize_subset (c, paint, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && paint.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c) const
  {
    TRACE_PAINT (this);
    c->funcs->push_inverse_root_transform (c->data, c->font);
    c->funcs->push_clip_glyph (c->data, gid, c->font);
    c->funcs->push_root_transform (c->data, c->font);
    c->recurse (this+paint);
    c->funcs->pop_transform (c->data);
    c->funcs->pop_clip (c->data);
    c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 10 */
  Offset24To<Paint> paint;  /* Offset (from beginning of PaintGlyph table) to Paint subtable. */
  HBUINT16  gid;
  public:
  DEFINE_SIZE_STATIC (6);
};

struct PaintColrGlyph
{
  void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer HB_UNUSED) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  inline void paint_glyph (hb_paint_context_t *c) const;

  HBUINT8 format; /* format = 11 */
  HBUINT16 gid;
  public:
  DEFINE_SIZE_STATIC (3);
};

template <template<typenameclass Var>
struct PaintTransform
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);
    if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false);
    if (format == 13 && c->plan->all_axes_pinned)
      out->format = 12;
    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  src.sanitize (c, this) &&
                  transform.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c) const
  {
    TRACE_PAINT (this);
    (this+transform).paint_glyph (c); // This does a push_transform()
    c->recurse (this+src);
    c->funcs->pop_transform (c->data);
  }

  HBUINT8   format; /* format = 12(noVar) or 13 (Var) */
  Offset24To<Paint>  src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */
  Offset24To<Var<Affine2x3>> transform;
  public:
  DEFINE_SIZE_STATIC (7);
};

struct PaintTranslate
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->dx = dx + (int) roundf (instancer (varIdxBase, 0));
      out->dy = dy + (int) roundf (instancer (varIdxBase, 1));
    }

    if (format == 15 && c->plan->all_axes_pinned)
        out->format = 14;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float ddx = dx + c->instancer (varIdxBase, 0);
    float ddy = dy + c->instancer (varIdxBase, 1);

    bool p1 = c->funcs->push_translate (c->data, ddx, ddy);
    c->recurse (this+src);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 14(noVar) or 15 (Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */
  FWORD  dx;
  FWORD  dy;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size);
};

struct PaintScale
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
      out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
    }

    if (format == 17 && c->plan->all_axes_pinned)
        out->format = 16;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
    float sy = scaleY.to_float (c->instancer (varIdxBase, 1));

    bool p1 = c->funcs->push_scale (c->data, sx, sy);
    c->recurse (this+src);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 16 (noVar) or 17(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintScale table) to Paint subtable. */
  F2DOT14  scaleX;
  F2DOT14  scaleY;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
};

struct PaintScaleAroundCenter
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
      out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
    }

    if (format == 19 && c->plan->all_axes_pinned)
        out->format = 18;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
    float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
    float tCenterX = centerX + c->instancer (varIdxBase, 2);
    float tCenterY = centerY + c->instancer (varIdxBase, 3);

    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
    bool p2 = c->funcs->push_scale (c->data, sx, sy);
    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
    c->recurse (this+src);
    if (p3) c->funcs->pop_transform (c->data);
    if (p2) c->funcs->pop_transform (c->data);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 18 (noVar) or 19(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */
  F2DOT14 scaleX;
  F2DOT14 scaleY;
  FWORD  centerX;
  FWORD  centerY;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
};

struct PaintScaleUniform
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
      out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));

    if (format == 21 && c->plan->all_axes_pinned)
        out->format = 20;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float s = scale.to_float (c->instancer (varIdxBase, 0));

    bool p1 = c->funcs->push_scale (c->data, s, s);
    c->recurse (this+src);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 20 (noVar) or 21(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */
  F2DOT14  scale;
  public:
  DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
};

struct PaintScaleUniformAroundCenter
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
    }

    if (format == 23 && c->plan->all_axes_pinned)
        out->format = 22;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float s = scale.to_float (c->instancer (varIdxBase, 0));
    float tCenterX = centerX + c->instancer (varIdxBase, 1);
    float tCenterY = centerY + c->instancer (varIdxBase, 2);

    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
    bool p2 = c->funcs->push_scale (c->data, s, s);
    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
    c->recurse (this+src);
    if (p3) c->funcs->pop_transform (c->data);
    if (p2) c->funcs->pop_transform (c->data);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 22 (noVar) or 23(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */
  F2DOT14 scale;
  FWORD  centerX;
  FWORD  centerY;
  public:
  DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
};

struct PaintRotate
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
      out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));

    if (format == 25 && c->plan->all_axes_pinned)
      out->format = 24;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float a = angle.to_float (c->instancer (varIdxBase, 0));

    bool p1 = c->funcs->push_rotate (c->data, a);
    c->recurse (this+src);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 24 (noVar) or 25(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */
  F2DOT14  angle;
  public:
  DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
};

struct PaintRotateAroundCenter
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
    }

    if (format ==27 && c->plan->all_axes_pinned)
        out->format = 26;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float a = angle.to_float (c->instancer (varIdxBase, 0));
    float tCenterX = centerX + c->instancer (varIdxBase, 1);
    float tCenterY = centerY + c->instancer (varIdxBase, 2);

    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
    bool p2 = c->funcs->push_rotate (c->data, a);
    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
    c->recurse (this+src);
    if (p3) c->funcs->pop_transform (c->data);
    if (p2) c->funcs->pop_transform (c->data);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 26 (noVar) or 27(Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */
  F2DOT14 angle;
  FWORD  centerX;
  FWORD  centerY;
  public:
  DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
};

struct PaintSkew
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
      out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
    }

    if (format == 29 && c->plan->all_axes_pinned)
        out->format = 28;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
    float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));

    bool p1 = c->funcs->push_skew (c->data, sx, sy);
    c->recurse (this+src);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 28(noVar) or 29 (Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */
  F2DOT14  xSkewAngle;
  F2DOT14  ySkewAngle;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
};

struct PaintSkewAroundCenter
{
  HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
      out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
    }

    if (format == 31 && c->plan->all_axes_pinned)
        out->format = 30;

    return_trace (out->src.serialize_subset (c, src, this, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && src.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
  {
    TRACE_PAINT (this);
    float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
    float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
    float tCenterX = centerX + c->instancer (varIdxBase, 2);
    float tCenterY = centerY + c->instancer (varIdxBase, 3);

    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
    bool p2 = c->funcs->push_skew (c->data, sx, sy);
    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
    c->recurse (this+src);
    if (p3) c->funcs->pop_transform (c->data);
    if (p2) c->funcs->pop_transform (c->data);
    if (p1) c->funcs->pop_transform (c->data);
  }

  HBUINT8  format; /* format = 30(noVar) or 31 (Var) */
  Offset24To<Paint> src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */
  F2DOT14 xSkewAngle;
  F2DOT14 ySkewAngle;
  FWORD  centerX;
  FWORD  centerY;
  public:
  DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
};

struct PaintComposite
{
  void closurev1 (hb_colrv1_closure_context_t* c) const;

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (this);
    if (unlikely (!out)) return_trace (false);

    bool ret = false;
    ret |= out->src.serialize_subset (c, src, this, instancer);
    ret |= out->backdrop.serialize_subset (c, backdrop, this, instancer);
    return_trace (ret);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
    c->check_ops (this->min_size) && // PainComposite can get exponential
                  src.sanitize (c, this) &&
                  backdrop.sanitize (c, this));
  }

  void paint_glyph (hb_paint_context_t *c) const
  {
    TRACE_PAINT (this);
    c->recurse (this+backdrop);
    c->funcs->push_group (c->data);
    c->recurse (this+src);
    c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
  }

  HBUINT8  format; /* format = 32 */
  Offset24To<Paint> src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */
  CompositeMode  mode;   /* If mode is unrecognized use COMPOSITE_CLEAR */
  Offset24To<Paint> backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */
  public:
  DEFINE_SIZE_STATIC (8);
};

struct ClipBoxData
{
  int xMin, yMin, xMax, yMax;
};

struct ClipBoxFormat1
{
  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const
  {
    clip_box.xMin = xMin;
    clip_box.yMin = yMin;
    clip_box.xMax = xMax;
    clip_box.yMax = yMax;
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer,
               uint32_t varIdxBase) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (*this);
    if (unlikely (!out)) return_trace (false);

    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
    {
      out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0));
      out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1));
      out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2));
      out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3));
    }

    if (format == 2 && c->plan->all_axes_pinned)
        out->format = 1;

    return_trace (true);
  }

  public:
  HBUINT8 format; /* format = 1(noVar) or 2(Var)*/
  FWORD  xMin;
  FWORD  yMin;
  FWORD  xMax;
  FWORD  yMax;
  public:
  DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size);
};

struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
{
  void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const
  {
    value.get_clip_box(clip_box, instancer);
    if (instancer)
    {
      clip_box.xMin += roundf (instancer (varIdxBase, 0));
      clip_box.yMin += roundf (instancer (varIdxBase, 1));
      clip_box.xMax += roundf (instancer (varIdxBase, 2));
      clip_box.yMax += roundf (instancer (varIdxBase, 3));
    }
  }

  void closurev1 (hb_colrv1_closure_context_t* c) const
  { c->variation_indices->add_range (varIdxBase, varIdxBase + 3); }
};

struct ClipBox
{
  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    switch (u.format) {
    case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION));
    case 2: return_trace (u.format2.subset (c, instancer));
    default:return_trace (c->default_return_value ());
    }
  }

  void closurev1 (hb_colrv1_closure_context_t* c) const
  {
    switch (u.format) {
    case 2: u.format2.closurev1 (c);
    default:return;
    }
  }

  template <typename context_t, typename ...Ts>
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
  {
    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
    TRACE_DISPATCH (this, u.format);
    switch (u.format) {
    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
    default:return_trace (c->default_return_value ());
    }
  }

  bool get_extents (hb_glyph_extents_t *extents,
                    const ItemVarStoreInstancer &instancer) const
  {
    ClipBoxData clip_box;
    switch (u.format) {
    case 1:
      u.format1.get_clip_box (clip_box, instancer);
      break;
    case 2:
      u.format2.get_clip_box (clip_box, instancer);
      break;
    default:
      return false;
    }

    extents->x_bearing = clip_box.xMin;
    extents->y_bearing = clip_box.yMax;
    extents->width = clip_box.xMax - clip_box.xMin;
    extents->height = clip_box.yMin - clip_box.yMax;
    return true;
  }

  protected:
  union {
  HBUINT8  format;         /* Format identifier */
  ClipBoxFormat1 format1;
  ClipBoxFormat2 format2;
  } u;
};

struct ClipRecord
{
  int cmp (hb_codepoint_t g) const
  { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }

  void closurev1 (hb_colrv1_closure_context_t* c, const void *base) const
  {
    if (!c->glyphs->intersects (startGlyphID, endGlyphID)) return;
    (base+clipBox).closurev1 (c);
  }

  bool subset (hb_subset_context_t *c,
               const void *base,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->embed (*this);
    if (unlikely (!out)) return_trace (false);

    return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && clipBox.sanitize (c, base));
  }

  bool get_extents (hb_glyph_extents_t *extents,
      const void *base,
      const ItemVarStoreInstancer &instancer) const
  {
    return (base+clipBox).get_extents (extents, instancer);
  }

  public:
  HBUINT16  startGlyphID;  // first gid clip applies to
  HBUINT16  endGlyphID;    // last gid clip applies to, inclusive
  Offset24To<ClipBox> clipBox;   // Box or VarBox
  public:
  DEFINE_SIZE_STATIC (7);
};
DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);

struct ClipList
{
  unsigned serialize_clip_records (hb_subset_context_t *c,
                                   const ItemVarStoreInstancer &instancer,
                                   const hb_set_t& gids,
                                   const hb_map_t& gid_offset_map) const
  {
    TRACE_SERIALIZE (this);
    if (gids.is_empty () ||
        gid_offset_map.get_population () != gids.get_population ())
      return_trace (0);

    unsigned count  = 0;

    hb_codepoint_t start_gid= gids.get_min ();
    hb_codepoint_t prev_gid = start_gid;

    unsigned offset = gid_offset_map.get (start_gid);
    unsigned prev_offset = offset;
    for (const hb_codepoint_t _ : gids.iter ())
    {
      if (_ == start_gid) continue;

      offset = gid_offset_map.get (_);
      if (_ == prev_gid + 1 &&  offset == prev_offset)
      {
        prev_gid = _;
        continue;
      }

      ClipRecord record;
      record.startGlyphID = start_gid;
      record.endGlyphID = prev_gid;
      record.clipBox = prev_offset;

      if (!record.subset (c, this, instancer)) return_trace (0);
      count++;

      start_gid = _;
      prev_gid = _;
      prev_offset = offset;
    }

    //last one
    {
      ClipRecord record;
      record.startGlyphID = start_gid;
      record.endGlyphID = prev_gid;
      record.clipBox = prev_offset;
      if (!record.subset (c, this, instancer)) return_trace (0);
      count++;
    }
    return_trace (count);
  }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->start_embed (*this);
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);

    const hb_set_t& glyphset = c->plan->_glyphset_colred;
    const hb_map_t &glyph_map = *c->plan->glyph_map;

    hb_map_t new_gid_offset_map;
    hb_set_t new_gids;
    for (const ClipRecord& record : clips.iter ())
    {
      unsigned start_gid = record.startGlyphID;
      unsigned end_gid = record.endGlyphID;
      for (unsigned gid = start_gid; gid <= end_gid; gid++)
      {
        if (!glyphset.has (gid) || !glyph_map.has (gid)) continue;
        unsigned new_gid = glyph_map.get (gid);
        new_gid_offset_map.set (new_gid, record.clipBox);
        new_gids.add (new_gid);
      }
    }

    unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map);
    if (!count) return_trace (false);
    return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    // TODO Make a formatted struct!
    return_trace (c->check_struct (this) && clips.sanitize (c, this));
  }

  bool
  get_extents (hb_codepoint_t gid,
        hb_glyph_extents_t *extents,
        const ItemVarStoreInstancer &instancer) const
  {
    auto *rec = clips.as_array ().bsearch (gid);
    if (rec)
    {
      rec->get_extents (extents, this, instancer);
      return true;
    }
    return false;
  }

  HBUINT8   format;  // Set to 1.
  SortedArray32Of<ClipRecord> clips;  // Clip records, sorted by startGlyphID
  public:
  DEFINE_SIZE_ARRAY_SIZED (5, clips);
};

struct Paint
{

  template <typename ...Ts>
  bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
  {
    TRACE_SANITIZE (this);

    if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL)))
      return_trace (c->no_dispatch_return_value ());

    return_trace (c->end_recursion (this->dispatch (c, std::forward<Ts> (ds)...)));
  }

  template <typename context_t, typename ...Ts>
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
  {
    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
    TRACE_DISPATCH (this, u.format);
    switch (u.format) {
    case 1: return_trace (c->dispatch (u.paintformat1, std::forward<Ts> (ds)...));
    case 2: return_trace (c->dispatch (u.paintformat2, std::forward<Ts> (ds)...));
    case 3: return_trace (c->dispatch (u.paintformat3, std::forward<Ts> (ds)...));
    case 4: return_trace (c->dispatch (u.paintformat4, std::forward<Ts> (ds)...));
    case 5: return_trace (c->dispatch (u.paintformat5, std::forward<Ts> (ds)...));
    case 6: return_trace (c->dispatch (u.paintformat6, std::forward<Ts> (ds)...));
    case 7: return_trace (c->dispatch (u.paintformat7, std::forward<Ts> (ds)...));
    case 8: return_trace (c->dispatch (u.paintformat8, std::forward<Ts> (ds)...));
    case 9: return_trace (c->dispatch (u.paintformat9, std::forward<Ts> (ds)...));
    case 10: return_trace (c->dispatch (u.paintformat10, std::forward<Ts> (ds)...));
    case 11: return_trace (c->dispatch (u.paintformat11, std::forward<Ts> (ds)...));
    case 12: return_trace (c->dispatch (u.paintformat12, std::forward<Ts> (ds)...));
    case 13: return_trace (c->dispatch (u.paintformat13, std::forward<Ts> (ds)...));
    case 14: return_trace (c->dispatch (u.paintformat14, std::forward<Ts> (ds)...));
    case 15: return_trace (c->dispatch (u.paintformat15, std::forward<Ts> (ds)...));
    case 16: return_trace (c->dispatch (u.paintformat16, std::forward<Ts> (ds)...));
    case 17: return_trace (c->dispatch (u.paintformat17, std::forward<Ts> (ds)...));
    case 18: return_trace (c->dispatch (u.paintformat18, std::forward<Ts> (ds)...));
    case 19: return_trace (c->dispatch (u.paintformat19, std::forward<Ts> (ds)...));
    case 20: return_trace (c->dispatch (u.paintformat20, std::forward<Ts> (ds)...));
    case 21: return_trace (c->dispatch (u.paintformat21, std::forward<Ts> (ds)...));
    case 22: return_trace (c->dispatch (u.paintformat22, std::forward<Ts> (ds)...));
    case 23: return_trace (c->dispatch (u.paintformat23, std::forward<Ts> (ds)...));
    case 24: return_trace (c->dispatch (u.paintformat24, std::forward<Ts> (ds)...));
    case 25: return_trace (c->dispatch (u.paintformat25, std::forward<Ts> (ds)...));
    case 26: return_trace (c->dispatch (u.paintformat26, std::forward<Ts> (ds)...));
    case 27: return_trace (c->dispatch (u.paintformat27, std::forward<Ts> (ds)...));
    case 28: return_trace (c->dispatch (u.paintformat28, std::forward<Ts> (ds)...));
    case 29: return_trace (c->dispatch (u.paintformat29, std::forward<Ts> (ds)...));
    case 30: return_trace (c->dispatch (u.paintformat30, std::forward<Ts> (ds)...));
    case 31: return_trace (c->dispatch (u.paintformat31, std::forward<Ts> (ds)...));
    case 32: return_trace (c->dispatch (u.paintformat32, std::forward<Ts> (ds)...));
    default:return_trace (c->default_return_value ());
    }
  }

  protected:
  union {
  HBUINT8     format;
  PaintColrLayers    paintformat1;
  NoVariable<PaintSolid>   paintformat2;
  Variable<PaintSolid>    paintformat3;
  NoVariable<PaintLinearGradient<NoVariable>> paintformat4;
  Variable<PaintLinearGradient<Variable>> paintformat5;
  NoVariable<PaintRadialGradient<NoVariable>> paintformat6;
  Variable<PaintRadialGradient<Variable>> paintformat7;
  NoVariable<PaintSweepGradient<NoVariable>> paintformat8;
  Variable<PaintSweepGradient<Variable>> paintformat9;
  PaintGlyph     paintformat10;
  PaintColrGlyph    paintformat11;
  PaintTransform<NoVariable>   paintformat12;
  PaintTransform<Variable>   paintformat13;
  NoVariable<PaintTranslate>   paintformat14;
  Variable<PaintTranslate>   paintformat15;
  NoVariable<PaintScale>   paintformat16;
  Variable<PaintScale>    paintformat17;
  NoVariable<PaintScaleAroundCenter>  paintformat18;
  Variable<PaintScaleAroundCenter>  paintformat19;
  NoVariable<PaintScaleUniform>   paintformat20;
  Variable<PaintScaleUniform>   paintformat21;
  NoVariable<PaintScaleUniformAroundCenter> paintformat22;
  Variable<PaintScaleUniformAroundCenter> paintformat23;
  NoVariable<PaintRotate>   paintformat24;
  Variable<PaintRotate>    paintformat25;
  NoVariable<PaintRotateAroundCenter>  paintformat26;
  Variable<PaintRotateAroundCenter>  paintformat27;
  NoVariable<PaintSkew>    paintformat28;
  Variable<PaintSkew>    paintformat29;
  NoVariable<PaintSkewAroundCenter>  paintformat30;
  Variable<PaintSkewAroundCenter>  paintformat31;
  PaintComposite    paintformat32;
  } u;
  public:
  DEFINE_SIZE_MIN (2);
};

struct BaseGlyphPaintRecord
{
  int cmp (hb_codepoint_t g) const
  { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }

  bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
                  const void* src_base, hb_subset_context_t *c,
                  const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SERIALIZE (this);
    auto *out = s->embed (this);
    if (unlikely (!out)) return_trace (false);
    if (!s->check_assign (out->glyphId, glyph_map->get (glyphId),
                          HB_SERIALIZE_ERROR_INT_OVERFLOW))
      return_trace (false);

    return_trace (out->paint.serialize_subset (c, paint, src_base, instancer));
  }

  bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (likely (c->check_struct (this) && paint.sanitize (c, base)));
  }

  public:
  HBGlyphID16  glyphId;    /* Glyph ID of reference glyph */
  Offset32To<Paint> paint;      /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint,
                                     * Typically PaintColrLayers */

  public:
  DEFINE_SIZE_STATIC (6);
};

struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
{
  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->start_embed (this);
    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
    const hb_set_t* glyphset = &c->plan->_glyphset_colred;

    for (const auto& _ : as_array ())
    {
      unsigned gid = _.glyphId;
      if (!glyphset->has (gid)) continue;

      if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++;
      else return_trace (false);
    }

    return_trace (out->len != 0);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (SortedArray32Of<BaseGlyphPaintRecord>::sanitize (c, this));
  }
};

struct LayerList : Array32OfOffset32To<Paint>
{
  const Paint& get_paint (unsigned i) const
  { return this+(*this)[i]; }

  bool subset (hb_subset_context_t *c,
               const ItemVarStoreInstancer &instancer) const
  {
    TRACE_SUBSET (this);
    auto *out = c->serializer->start_embed (this);
    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);

    bool ret = false;
    for (const auto& _ : + hb_enumerate (*this)
                         | hb_filter (c->plan->colrv1_layers, hb_first))

    {
      auto *o = out->serialize_append (c->serializer);
      if (unlikely (!o)) return_trace (false);
      ret |= o->serialize_subset (c, _.second, this, instancer);
    }
    return_trace (ret);
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (Array32OfOffset32To<Paint>::sanitize (c, this));
  }
};

struct delta_set_index_map_subset_plan_t
{
  unsigned get_inner_bit_count () const { return inner_bit_count; }
  unsigned get_width ()           const { return ((outer_bit_count + inner_bit_count + 7) / 8); }
  hb_array_t<const uint32_t> get_output_map () const { return output_map.as_array (); }

  delta_set_index_map_subset_plan_t (const hb_map_t &new_deltaset_idx_varidx_map)
  {
    map_count = 0;
    outer_bit_count = 0;
    inner_bit_count = 1;
    output_map.init ();

    /* search backwards */
    unsigned count = new_deltaset_idx_varidx_map.get_population ();
    if (!count) return;

    unsigned last_idx = (unsigned)-1;
    unsigned last_varidx = (unsigned)-1;

    for (unsigned i = count; i; i--)
    {
      unsigned delta_set_idx = i - 1;
      unsigned var_idx = new_deltaset_idx_varidx_map.get (delta_set_idx);
      if (i == count)
      {
        last_idx = delta_set_idx;
        last_varidx = var_idx;
        continue;
      }
      if (var_idx != last_varidx)
        break;
      last_idx = delta_set_idx;
    }

    map_count = last_idx + 1;
  }

  bool remap (const hb_map_t &new_deltaset_idx_varidx_map)
  {
    /* recalculate bit_count */
    outer_bit_count = 1;
    inner_bit_count = 1;

    if (unlikely (!output_map.resize (map_count, false))) return false;

    for (unsigned idx = 0; idx < map_count; idx++)
    {
      uint32_t *var_idx;
      if (!new_deltaset_idx_varidx_map.has (idx, &var_idx)) return false;
      output_map.arrayZ[idx] = *var_idx;

      unsigned outer = (*var_idx) >> 16;
      unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
      outer_bit_count = hb_max (bit_count, outer_bit_count);

      unsigned inner = (*var_idx) & 0xFFFF;
      bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
      inner_bit_count = hb_max (bit_count, inner_bit_count);
    }
    return true;
  }

  private:
  unsigned map_count;
  unsigned outer_bit_count;
  unsigned inner_bit_count;
  hb_vector_t<uint32_t> output_map;
};

struct COLR
{
  static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;

  bool has_v0_data () const { return numBaseGlyphs; }
  bool has_v1_data () const
  {
    if (version < 1)
      return false;
    hb_barrier ();

    return (this+baseGlyphList).len > 0;
  }

  unsigned int get_glyph_layers (hb_codepoint_t       glyph,
     unsigned int         start_offset,
     unsigned int        *count, /* IN/OUT.  May be NULL. */
     hb_ot_color_layer_t *layers /* OUT.     May be NULL. */) const
  {
    const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph);

    hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers);
    hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
               record.numLayers);
    if (count)
    {
      + glyph_layers.sub_array (start_offset, count)
      | hb_sink (hb_array (layers, *count))
      ;
    }
    return glyph_layers.length;
  }

  struct accelerator_t
  {
    accelerator_t (hb_face_t *face)
    { colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
    ~accelerator_t () { this->colr.destroy (); }

    bool is_valid () { return colr.get_blob ()->length; }

    void closure_glyphs (hb_codepoint_t glyph,
    hb_set_t *related_ids /* OUT */) const
    { colr->closure_glyphs (glyph, related_ids); }

    void closure_V0palette_indices (const hb_set_t *glyphs,
        hb_set_t *palettes /* OUT */) const
    { colr->closure_V0palette_indices (glyphs, palettes); }

    void closure_forV1 (hb_set_t *glyphset,
                        hb_set_t *layer_indices,
                        hb_set_t *palette_indices,
                        hb_set_t *variation_indices,
                        hb_set_t *delta_set_indices) const
    { colr->closure_forV1 (glyphset, layer_indices, palette_indices, variation_indices, delta_set_indices); }

    bool has_var_store () const
    { return colr->has_var_store (); }

    const ItemVariationStore &get_var_store () const
    { return colr->get_var_store (); }

    bool has_delta_set_index_map () const
    { return colr->has_delta_set_index_map (); }

    const DeltaSetIndexMap &get_delta_set_index_map () const
    { return colr->get_delta_set_index_map (); }

    private:
    hb_blob_ptr_t<COLR> colr;
  };

  void closure_glyphs (hb_codepoint_t glyph,
         hb_set_t *related_ids /* OUT */) const
  {
    const BaseGlyphRecord *record = get_base_glyph_record (glyph);
    if (!record) return;

    auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx,
               record->numLayers);
    if (!glyph_layers.length) return;
    related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size);
  }

  void closure_V0palette_indices (const hb_set_t *glyphs,
      hb_set_t *palettes /* OUT */) const
  {
    if (!numBaseGlyphs || !numLayers) return;
    hb_array_t<const BaseGlyphRecord> baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs);
    hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers);

    for (const BaseGlyphRecord record : baseGlyphs)
    {
      if (!glyphs->has (record.glyphId)) continue;
      hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
                                                                   record.numLayers);
      for (const LayerRecord layer : glyph_layers)
        palettes->add (layer.colorIdx);
    }
  }

  void closure_forV1 (hb_set_t *glyphset,
                      hb_set_t *layer_indices,
                      hb_set_t *palette_indices,
                      hb_set_t *variation_indices,
                      hb_set_t *delta_set_indices) const
  {
    if (version < 1) return;
    hb_barrier ();

    hb_set_t visited_glyphs;

    hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices, variation_indices);
    const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;

    for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ())
    {
      unsigned gid = baseglyph_paintrecord.glyphId;
      if (!glyphset->has (gid)) continue;

      const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint;
      paint.dispatch (&c);
    }
    hb_set_union (glyphset, &visited_glyphs);

    const ClipList &cliplist = this+clipList;
    c.glyphs = glyphset;
    for (const ClipRecord &clip_record : cliplist.clips.iter())
      clip_record.closurev1 (&c, &cliplist);

    // if a DeltaSetIndexMap is included, collected variation indices are
    // actually delta set indices, we need to map them into variation indices
    if (has_delta_set_index_map ())
    {
      const DeltaSetIndexMap &var_idx_map = this+varIdxMap;
      delta_set_indices->set (*variation_indices);
      variation_indices->clear ();
      for (unsigned delta_set_idx : *delta_set_indices)
        variation_indices->add (var_idx_map.map (delta_set_idx));
    }
  }

  const LayerList& get_layerList () const
  { return (this+layerList); }

  const BaseGlyphList& get_baseglyphList () const
  { return (this+baseGlyphList); }

  bool has_var_store () const
  { return version >= 1 && hb_barrier () && varStore != 0; }

  bool has_delta_set_index_map () const
  { return version >= 1 && hb_barrier () && varIdxMap != 0; }

  bool has_clip_list () const
  { return version >= 1 && hb_barrier () && clipList != 0; }

  const DeltaSetIndexMap &get_delta_set_index_map () const
  { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }

  const ItemVariationStore &get_var_store () const
  { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }

  const ClipList &get_clip_list () const
  { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
    hb_barrier () &&
                  (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
                  (this+layersZ).sanitize (c, numLayers) &&
                  (version == 0 ||
     (hb_barrier () &&
      baseGlyphList.sanitize (c, this) &&
      layerList.sanitize (c, this) &&
      clipList.sanitize (c, this) &&
      varIdxMap.sanitize (c, this) &&
      varStore.sanitize (c, this))));
  }

  template<typename BaseIterator, typename LayerIterator,
    hb_requires (hb_is_iterator (BaseIterator)),
    hb_requires (hb_is_iterator (LayerIterator))>
  bool serialize_V0 (hb_serialize_context_t *c,
       unsigned version,
       BaseIterator base_it,
       LayerIterator layer_it)
  {
    TRACE_SERIALIZE (this);
    if (unlikely (base_it.len () != layer_it.len ()))
      return_trace (false);

    this->version = version;
    numLayers = 0;
    numBaseGlyphs = base_it.len ();
    if (numBaseGlyphs == 0)
    {
      baseGlyphsZ = 0;
      layersZ = 0;
      return_trace (true);
    }

    c->push ();
    for (const hb_item_type<BaseIterator> _ : + base_it.iter ())
    {
      auto* record = c->embed (_);
      if (unlikely (!record)) return_trace (false);
      record->firstLayerIdx = numLayers;
      numLayers += record->numLayers;
    }
    c->add_link (baseGlyphsZ, c->pop_pack ());

    c->push ();
    for (const hb_item_type<LayerIterator>& _ : + layer_it.iter ())
      _.as_array ().copy (c);

    c->add_link (layersZ, c->pop_pack ());

    return_trace (true);
  }

  const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const
  {
    const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid);
    if (record == &Null (BaseGlyphRecord) ||
        (record && (hb_codepoint_t) record->glyphId != gid))
      record = nullptr;
    return record;
  }

  const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const
  {
    const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid);
    if ((record && (hb_codepoint_t) record->glyphId != gid))
      record = nullptr;
    return record;
  }

  bool downgrade_to_V0 (const hb_set_t &glyphset) const
  {
    //no more COLRv1 glyphs, downgrade to version 0
    for (const BaseGlyphPaintRecord& _ : get_baseglyphList ())
      if (glyphset.has (_.glyphId))
        return false;

    return true;
  }

  bool subset_varstore (hb_subset_context_t *c,
                        COLR* out /* OUT */) const
  {
    TRACE_SUBSET (this);
    if (!varStore || c->plan->all_axes_pinned ||
        !c->plan->colrv1_variation_idx_delta_map)
      return_trace (true);

    const ItemVariationStore& var_store = this+varStore;
    if (c->plan->normalized_coords)
    {
      item_variations_t item_vars;
      /* turn off varstore optimization when varIdxMap is null, so we maintain
       * original var_idx sequence */

      bool optimize = (varIdxMap != 0) ? true : false;
      if (!item_vars.instantiate (var_store, c->plan,
                                  optimize, /* optimization */
                                  optimize, /* use_no_variation_idx = false */
                                  c->plan->colrv1_varstore_inner_maps.as_array ()))
        return_trace (false);

      /* do not serialize varStore if there's no variation data after
       * instancing: region_list or var_data is empty */

      if (item_vars.get_region_list () &&
          item_vars.get_vardata_encodings () &&
          !out->varStore.serialize_serialize (c->serializer,
                                              item_vars.has_long_word (),
                                              c->plan->axis_tags,
                                              item_vars.get_region_list (),
                                              item_vars.get_vardata_encodings ()))
        return_trace (false);

      /* if varstore is optimized, update colrv1_new_deltaset_idx_varidx_map in
       * subset plan.
       * If varstore is empty after instancing, varidx_map would be empty and
       * all var_idxes will be updated to VarIdx::NO_VARIATION */

      if (optimize)
      {
        const hb_map_t &varidx_map = item_vars.get_varidx_map ();
        for (auto _ : c->plan->colrv1_new_deltaset_idx_varidx_map.iter_ref ())
        {
          uint32_t varidx = _.second;
          uint32_t *new_varidx;
          if (varidx_map.has (varidx, &new_varidx))
            _.second = *new_varidx;
          else
            _.second = VarIdx::NO_VARIATION;
        }
      }
    }
    else
    {
      if (unlikely (!out->varStore.serialize_serialize (c->serializer,
                                                        &var_store,
                                                        c->plan->colrv1_varstore_inner_maps.as_array ())))
        return_trace (false);
    }

    return_trace (true);
  }

  bool subset_delta_set_index_map (hb_subset_context_t *c,
                                   COLR* out /* OUT */) const
  {
    TRACE_SUBSET (this);
    if (!varIdxMap || c->plan->all_axes_pinned ||
        !c->plan->colrv1_new_deltaset_idx_varidx_map)
      return_trace (true);

    const hb_map_t &deltaset_idx_varidx_map = c->plan->colrv1_new_deltaset_idx_varidx_map;
    delta_set_index_map_subset_plan_t index_map_plan (deltaset_idx_varidx_map);

    if (unlikely (!index_map_plan.remap (deltaset_idx_varidx_map)))
      return_trace (false);

    return_trace (out->varIdxMap.serialize_serialize (c->serializer, index_map_plan));
  }

  bool subset (hb_subset_context_t *c) const
  {
    TRACE_SUBSET (this);
    const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
    const hb_set_t& glyphset = c->plan->_glyphset_colred;

    auto base_it =
    + hb_range (c->plan->num_output_glyphs ())
    | hb_filter ([&](hb_codepoint_t new_gid)
   {
      hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
      if (glyphset.has (old_gid)) return true;
      return false;
   })
    | hb_map_retains_sorting ([&](hb_codepoint_t new_gid)
         {
    hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);

    const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
    if (unlikely (!old_record))
      return hb_pair_t<bool, BaseGlyphRecord> (false, Null (BaseGlyphRecord));
    BaseGlyphRecord new_record = {};
    new_record.glyphId = new_gid;
    new_record.numLayers = old_record->numLayers;
    return hb_pair_t<bool, BaseGlyphRecord> (true, new_record);
         })
    | hb_filter (hb_first)
    | hb_map_retains_sorting (hb_second)
    ;

    auto layer_it =
    + hb_range (c->plan->num_output_glyphs ())
    | hb_map (reverse_glyph_map)
    | hb_filter (glyphset)
    | hb_map_retains_sorting ([&](hb_codepoint_t old_gid)
         {
    const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
    hb_vector_t<LayerRecord> out_layers;

    if (unlikely (!old_record ||
           old_record->firstLayerIdx >= numLayers ||
           old_record->firstLayerIdx + old_record->numLayers > numLayers))
      return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);

    auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx,
                old_record->numLayers);
    out_layers.resize (layers.length);
    for (unsigned int i = 0; i < layers.length; i++) {
      out_layers[i] = layers[i];
      hb_codepoint_t new_gid = 0;
      if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
        return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
      out_layers[i].glyphId = new_gid;
      out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx);
    }

    return hb_pair_t<bool, hb_vector_t<LayerRecord>> (true, out_layers);
         })
    | hb_filter (hb_first)
    | hb_map_retains_sorting (hb_second)
    ;

    if (version == 0 && (!base_it || !layer_it))
      return_trace (false);

    auto *colr_prime = c->serializer->start_embed<COLR> ();
    if (unlikely (!c->serializer->extend_min (colr_prime)))  return_trace (false);

    if (version == 0 || downgrade_to_V0 (glyphset))
      return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));

    hb_barrier ();

    //start version 1
    if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false);
    if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);

    /* subset ItemVariationStore first, cause varidx_map needs to be updated
     * after instancing */

    if (!subset_varstore (c, colr_prime)) return_trace (false);

    ItemVarStoreInstancer instancer (&(get_var_store ()),
                          &(get_delta_set_index_map ()),
                          c->plan->normalized_coords.as_array ());

    if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
      return_trace (false);

    colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
    colr_prime->clipList.serialize_subset (c, clipList, this, instancer);

    return_trace (subset_delta_set_index_map (c, colr_prime));
  }

  const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
  {
    const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
    const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph);
    if (record)
    {
      const Paint &paint = &baseglyph_paintrecords+record->paint;
      return &paint;
    }
    else
      return nullptr;
  }

#ifndef HB_NO_PAINT
  bool
  get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
  {

    ItemVarStoreInstancer instancer (&(get_var_store ()),
                                     &(get_delta_set_index_map ()),
                                     hb_array (font->coords, font->num_coords));

    if (get_clip (glyph, extents, instancer))
    {
      font->scale_glyph_extents (extents);
      return true;
    }

    auto *extents_funcs = hb_paint_extents_get_funcs ();
    hb_paint_extents_context_t extents_data;
    bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));

    hb_extents_t e = extents_data.get_extents ();
    if (e.is_void ())
    {
      extents->x_bearing = 0;
      extents->y_bearing = 0;
      extents->width = 0;
      extents->height = 0;
    }
    else
    {
      extents->x_bearing = e.xmin;
      extents->y_bearing = e.ymax;
      extents->width = e.xmax - e.xmin;
      extents->height = e.ymin - e.ymax;
    }

    return ret;
  }
#endif

  bool
  has_paint_for_glyph (hb_codepoint_t glyph) const
  {
    if (version >= 1)
    {
      hb_barrier ();

      const Paint *paint = get_base_glyph_paint (glyph);

      return paint != nullptr;
    }

    return false;
  }

  bool get_clip (hb_codepoint_t glyph,
   hb_glyph_extents_t *extents,
   const ItemVarStoreInstancer instancer) const
  {
    return get_clip_list ().get_extents (glyph,
     extents,
     instancer);
  }

#ifndef HB_NO_PAINT
  bool
  paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = trueconst
  {
    ItemVarStoreInstancer instancer (&(get_var_store ()),
                          &(get_delta_set_index_map ()),
                          hb_array (font->coords, font->num_coords));
    hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
    c.current_glyphs.add (glyph);

    if (version >= 1)
    {
      hb_barrier ();

      const Paint *paint = get_base_glyph_paint (glyph);
      if (paint)
      {
        // COLRv1 glyph

 bool is_bounded = true;
 if (clip)
 {
   hb_glyph_extents_t extents;
   if (get_clip (glyph, &extents, instancer))
   {
     font->scale_glyph_extents (&extents);
     c.funcs->push_clip_rectangle (c.data,
       extents.x_bearing,
       extents.y_bearing + extents.height,
       extents.x_bearing + extents.width,
       extents.y_bearing);
   }
   else
   {
     auto *extents_funcs = hb_paint_extents_get_funcs ();
     hb_paint_extents_context_t extents_data;

     paint_glyph (font, glyph,
    extents_funcs, &extents_data,
    palette_index, foreground,
    false);

     hb_extents_t extents = extents_data.get_extents ();
     is_bounded = extents_data.is_bounded ();

     c.funcs->push_clip_rectangle (c.data,
       extents.xmin,
       extents.ymin,
       extents.xmax,
       extents.ymax);
   }
 }

 c.funcs->push_root_transform (c.data, font);

 if (is_bounded)
   c.recurse (*paint);

 c.funcs->pop_transform (c.data);

 if (clip)
   c.funcs->pop_clip (c.data);

        return true;
      }
    }

    const BaseGlyphRecord *record = get_base_glyph_record (glyph);
    if (record && ((hb_codepoint_t) record->glyphId == glyph))
    {
      // COLRv0 glyph
      for (const auto &r : (this+layersZ).as_array (numLayers)
      .sub_array (record->firstLayerIdx, record->numLayers))
      {
        hb_bool_t is_foreground;
        hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground);
        c.funcs->push_clip_glyph (c.data, r.glyphId, c.font);
        c.funcs->color (c.data, is_foreground, color);
        c.funcs->pop_clip (c.data);
      }

      return true;
    }

    return false;
  }
#endif

  protected:
  HBUINT16 version; /* Table version number (starts at 0). */
  HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
  NNOffset32To<SortedUnsizedArrayOf<BaseGlyphRecord>>
  baseGlyphsZ; /* Offset to Base Glyph records. */
  NNOffset32To<UnsizedArrayOf<LayerRecord>>
  layersZ; /* Offset to Layer Records. */
  HBUINT16 numLayers; /* Number of Layer Records. */
  // Version-1 additions
  Offset32To<BaseGlyphList>  baseGlyphList;
  Offset32To<LayerList>   layerList;
  Offset32To<ClipList>   clipList;   // Offset to ClipList table (may be NULL)
  Offset32To<DeltaSetIndexMap>  varIdxMap;  // Offset to DeltaSetIndexMap table (may be NULL)
  Offset32To<ItemVariationStore> varStore;
  public:
  DEFINE_SIZE_MIN (14);
};

struct COLR_accelerator_t : COLR::accelerator_t {
  COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
};

void
hb_paint_context_t::recurse (const Paint &paint)
{
  if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
  depth_left--;
  edge_count--;
  paint.dispatch (this);
  depth_left++;
}

void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
{
  TRACE_PAINT (this);
  const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
  for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
  {
    if (unlikely (c->current_layers.has (i)))
      continue;

    c->current_layers.add (i);

    const Paint &paint = paint_offset_lists.get_paint (i);
    c->funcs->push_group (c->data);
    c->recurse (paint);
    c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);

    c->current_layers.del (i);
  }
}

void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
{
  TRACE_PAINT (this);

  if (unlikely (c->current_glyphs.has (gid)))
    return;

  c->current_glyphs.add (gid);

  c->funcs->push_inverse_root_transform (c->data, c->font);
  if (c->funcs->color_glyph (c->data, gid, c->font))
  {
    c->funcs->pop_transform (c->data);
    c->current_glyphs.del (gid);
    return;
  }
  c->funcs->pop_transform (c->data);

  const COLR *colr_table = c->get_colr_table ();
  const Paint *paint = colr_table->get_base_glyph_paint (gid);

  hb_glyph_extents_t extents = {0};
  bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer);

  if (has_clip_box)
    c->funcs->push_clip_rectangle (c->data,
       extents.x_bearing,
       extents.y_bearing + extents.height,
       extents.x_bearing + extents.width,
       extents.y_bearing);

  if (paint)
    c->recurse (*paint);

  if (has_clip_box)
    c->funcs->pop_clip (c->data);

  c->current_glyphs.del (gid);
}

/* namespace OT */

#endif /* OT_COLOR_COLR_COLR_HH */

Messung V0.5 in Prozent
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.52 Sekunden  (vorverarbeitet am  2026-04-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.