/*
 * 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-decycler.hh"
#include "../../../hb-open-type.hh"
#include "../../../hb-ot-var-common.hh"
#include "../../../hb-paint.hh"
#include "../../../hb-paint-bounded.hh"
#include "../../../hb-paint-extents.hh"

#include "../CPAL/CPAL.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;
}

struct hb_colr_scratch_t
{
  hb_paint_bounded_context_t paint_bounded;
  hb_paint_extents_context_t paint_extents;
};

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;
  hb_array_t<const BGRAColor> palette;
  hb_color_t foreground;
  ItemVarStoreInstancer &instancer;
  hb_decycler_t glyphs_decycler;
  hb_decycler_t layers_decycler;
  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 (
#ifndef HB_NO_COLOR
	     // https://github.com/harfbuzz/harfbuzz/issues/5116
	     font->face->table.CPAL->get_palette_colors (palette_ < font->face->table.CPAL->get_palette_count () ? palette_ : 0)
#endif
    ),
    foreground (foreground_),
    instancer (instancer_)
  {
    if (font->is_synthetic)
    {
      font = hb_font_create_sub_font (font);
      hb_font_set_synthetic_bold (font, 0, 0, true);
      hb_font_set_synthetic_slant (font, 0);
    }
    else
      hb_font_reference (font);
  }

  ~hb_paint_context_t ()
  {
    hb_font_destroy (font);
  }

  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))
        color = palette[color_index];

      *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)
  {
    if (num_of_layers == 0) return;
    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<unsigned, int> *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<typename> class 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);

    uint32_t first_layer_index = numLayers ? c->plan->colrv1_layers.get (firstLayerIndex) : 0;
    return_trace (c->serializer->check_assign (out->firstLayerIndex, first_layer_index,
                                               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 = 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<typename> class 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<typename> class 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<typename> class 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_font_transform (c->data, c->font);
    c->funcs->push_clip_glyph (c->data, gid, c->font);
    c->funcs->push_font_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<typename> class 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);

    c->funcs->push_translate (c->data, ddx, ddy);
    c->recurse (this+src);
    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));

    c->funcs->push_scale (c->data, sx, sy);
    c->recurse (this+src);
    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);

    c->funcs->push_scale_around_center (c->data, sx, sy, tCenterX, tCenterY);
    c->recurse (this+src);
    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));

    c->funcs->push_scale (c->data, s, s);
    c->recurse (this+src);
    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);

    c->funcs->push_scale_around_center (c->data, s, s, tCenterX, tCenterY);
    c->recurse (this+src);
    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));

    c->funcs->push_rotate (c->data, a);
    c->recurse (this+src);
    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);

    c->funcs->push_rotate_around_center (c->data, a, tCenterX, tCenterY);
    c->recurse (this+src);
    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));

    c->funcs->push_skew (c->data, sx, sy);
    c->recurse (this+src);
    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);

    c->funcs->push_skew_around_center (c->data, sx, sy, tCenterX, tCenterY);
    c->recurse (this+src);
    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->funcs->push_group (c->data);
    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);
    c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
  }

  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); return;
    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_data () const { return has_v0_data () || version; }

  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 ()
    {
      auto *scratch = cached_scratch.get_relaxed ();
      if (scratch)
      {
	scratch->~hb_colr_scratch_t ();
	hb_free (scratch);
      }

      colr.destroy ();
    }


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

#ifndef HB_NO_PAINT
    bool
    get_extents (hb_font_t *font,
		 hb_codepoint_t glyph,
		 hb_glyph_extents_t *extents) const
    {
      if (unlikely (!has_data ())) return false;

      hb_colr_scratch_t *scratch = acquire_scratch ();
      if (unlikely (!scratch)) return true;
      bool ret = colr->get_extents (font, glyph, extents, *scratch);
      release_scratch (scratch);
      return ret;
    }

    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 = true) const
    {
      if (unlikely (!has_data ())) return false;

      hb_colr_scratch_t *scratch = acquire_scratch ();
      if (unlikely (!scratch)) return true;
      bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch);
      release_scratch (scratch);
      return ret;
    }
#endif

    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 (); }
    const ItemVariationStore *get_var_store_ptr () const
    { return colr->get_var_store_ptr (); }

    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 (); }
    const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
    { return colr->get_delta_set_index_map_ptr (); }

    private:

    hb_colr_scratch_t *acquire_scratch () const
    {
      hb_colr_scratch_t *scratch = cached_scratch.get_acquire ();

      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
      {
	scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t));
	if (unlikely (!scratch))
	  return nullptr;
      }

      return scratch;
    }
    void release_scratch (hb_colr_scratch_t *scratch) const
    {
      if (!cached_scratch.cmpexch (nullptr, scratch))
      {
	scratch->~hb_colr_scratch_t ();
	hb_free (scratch);
      }
    }

    public:
    hb_blob_ptr_t<COLR> colr;
    private:
    mutable hb_atomic_t<hb_colr_scratch_t *> cached_scratch;
  };

  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 DeltaSetIndexMap *get_delta_set_index_map_ptr () const
  { return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }

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

  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_ptr (),
				     get_delta_set_index_map_ptr (),
				     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,
	       hb_colr_scratch_t &scratch) const
  {

    ItemVarStoreInstancer instancer (get_var_store_ptr (),
                                     get_delta_set_index_map_ptr (),
                                     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 ();
    scratch.paint_extents.clear ();
    bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch);

    auto e = scratch.paint_extents.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,
	       hb_colr_scratch_t &scratch) const
  {
    ItemVarStoreInstancer instancer (get_var_store_ptr (),
				     get_delta_set_index_map_ptr (),
				     hb_array (font->coords,
					       font->has_nonzero_coords ? font->num_coords : 0));
    hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);

    hb_decycler_node_t node (c.glyphs_decycler);
    node.visit (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
	  {
	    clip = false;
	    is_bounded = false;
	  }

	  if (!is_bounded)
	  {
	    auto *bounded_funcs = hb_paint_bounded_get_funcs ();
	    scratch.paint_bounded.clear ();

	    paint_glyph (font, glyph,
			 bounded_funcs, &scratch.paint_bounded,
			 palette_index, foreground,
			 false,
			 scratch);

	    is_bounded = scratch.paint_bounded.is_bounded ();
	  }
	}

	c.funcs->push_font_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 ();
  hb_decycler_node_t node (c->layers_decycler);
  for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
  {
    if (unlikely (!node.visit (i)))
      return;

    const Paint &paint = paint_offset_lists.get_paint (i);
    c->recurse (paint);
  }
}

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

  hb_decycler_node_t node (c->glyphs_decycler);
  if (unlikely (!node.visit (gid)))
    return;

  c->funcs->push_inverse_font_transform (c->data, c->font);
  if (c->funcs->color_glyph (c->data, gid, c->font))
  {
    c->funcs->pop_transform (c->data);
    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);
}

} /* namespace OT */

#endif /* OT_COLOR_COLR_COLR_HH */
