Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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
*/
#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<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
// Blend modes
// Modes that, uniquely, do not operate on components
};
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<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_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<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);
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 = true) const
{
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 */