/* * 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 HB_OT_COLOR_COLR_TABLE_HH #define HB_OT_COLOR_COLR_TABLE_HH #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" #include "hb-ot-var-common.hh" #include "hb-paint.hh" #include /* * COLR -- Color * https://docs.microsoft.com/en-us/typography/opentype/spec/colr */ #define HB_OT_TAG_COLR HB_TAG('C','O','L','R') #ifndef HB_COLRV1_MAX_NESTING_LEVEL #define HB_COLRV1_MAX_NESTING_LEVEL 128 #endif namespace OT { struct hb_paint_context_t; } struct hb_color_line_t { struct OT::hb_paint_context_t *c; const void *base; bool is_variable; }; namespace OT { struct COLR; struct Paint; struct hb_paint_context_t : hb_dispatch_context_t { template 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 (base); } public: const void *base; hb_paint_funcs_t *funcs; void *data; VarStoreInstancer &instancer; int depth_left = HB_COLRV1_MAX_NESTING_LEVEL; hb_paint_context_t (const void *base_, hb_paint_funcs_t *funcs_, void *data_, VarStoreInstancer &instancer_) : base (base_), funcs (funcs_), data (data_), instancer (instancer_) {} inline void recurse (const Paint &paint); }; struct hb_colrv1_closure_context_t : hb_dispatch_context_t { template 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 (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); } public: const void *base; hb_set_t visited_paint; hb_set_t *glyphs; hb_set_t *layer_indices; hb_set_t *palette_indices; 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_, unsigned nesting_level_left_ = HB_COLRV1_MAX_NESTING_LEVEL) : base (base_), glyphs (glyphs_), layer_indices (layer_indices_), palette_indices (palette_indices_), 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 struct Variable { static constexpr bool is_variable = true; Variable* 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 { TRACE_SUBSET (this); if (!value.subset (c)) return_trace (false); return_trace (c->serializer->embed (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 { value.paint_glyph (c, varIdxBase); } void get_color_stop (hb_color_stop_t *c, const VarStoreInstancer &instancer) const { value.get_color_stop (c, varIdxBase, instancer); } hb_paint_extend_t get_extend () const { return value.get_extend (); } protected: T value; public: VarIdx varIdxBase; public: DEFINE_SIZE_STATIC (4 + T::static_size); }; template struct NoVariable { static constexpr bool is_variable = false; static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; NoVariable* 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 { TRACE_SUBSET (this); return_trace (value.subset (c)); } 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 { value.paint_glyph (c, varIdxBase); } void get_color_stop (hb_color_stop_t *c, const VarStoreInstancer &instancer) const { value.get_color_stop (c, VarIdx::NO_VARIATION, instancer); } hb_paint_extend_t get_extend () const { return value.get_extend (); } T value; public: DEFINE_SIZE_STATIC (T::static_size); }; // Color structures struct ColorStop { void closurev1 (hb_colrv1_closure_context_t* c) const { c->add_palette_index (paletteIndex); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); 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_color_stop_t *out, uint32_t varIdx, const VarStoreInstancer &instancer) const { out->offset = stopOffset.to_float(instancer (varIdx, 0)); out->color_index = paletteIndex; out->alpha = alpha.to_float(instancer (varIdx, 1)); } 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 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 { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); if (unlikely (!out)) return_trace (false); 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)) 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 (unsigned int start, unsigned int *count, hb_color_stop_t *color_stops, const VarStoreInstancer &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 (&color_stops[i], instancer); *count = i; } return len; } hb_paint_extend_t get_extend () const { return (hb_paint_extend_t) (unsigned int) extend; } Extend extend; Array16Of> 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 paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const { 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 { 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)); } HB_INTERNAL 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); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); 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 { c->funcs->color (c->data, paletteIndex, alpha.to_float (c->instancer (varIdxBase, 0))); } HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ HBUINT16 paletteIndex; F2DOT14 alpha; public: DEFINE_SIZE_STATIC (3 + F2DOT14::static_size); }; template class Var> struct PaintLinearGradient { void closurev1 (hb_colrv1_closure_context_t* c) const { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->colorLine.serialize_subset (c, colorLine, this)); } 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 { hb_color_line_t cl = { c, &(this+colorLine), Var::is_variable }; 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; /* 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 class Var> struct PaintRadialGradient { void closurev1 (hb_colrv1_closure_context_t* c) const { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->colorLine.serialize_subset (c, colorLine, this)); } 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 { hb_color_line_t cl = { c, &(this+colorLine), Var::is_variable }; 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; /* 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 class Var> struct PaintSweepGradient { void closurev1 (hb_colrv1_closure_context_t* c) const { (this+colorLine).closurev1 (c); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->colorLine.serialize_subset (c, colorLine, this)); } 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 { hb_color_line_t cl = { c, &(this+colorLine), Var::is_variable }; 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) * (float) M_PI, (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * (float) M_PI); } HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ Offset24To> 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 { 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)); } 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 { c->funcs->push_clip_glyph (c->data, gid); c->recurse (this+paint); c->funcs->pop_clip (c->data); } HBUINT8 format; /* format = 10 */ Offset24To 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 { 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)); } HB_INTERNAL void paint_glyph (hb_paint_context_t *c) const; HBUINT8 format; /* format = 11 */ HBUINT16 gid; public: DEFINE_SIZE_STATIC (3); }; template class Var> struct PaintTransform { HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); if (!out->transform.serialize_copy (c->serializer, transform, this)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { (this+transform).paint_glyph (c); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ Offset24To> 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { c->funcs->push_transform (c->data, 1., 0., 0., 1., dx + c->instancer (varIdxBase, 0), dy + c->instancer (varIdxBase, 0)); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { c->funcs->push_transform (c->data, scaleX.to_float (c->instancer (varIdxBase, 0)), 0., 0., scaleY.to_float (c->instancer (varIdxBase, 1)), 0., 0.); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float tCenterX = centerX + c->instancer (varIdxBase, 2); float tCenterY = centerY + c->instancer (varIdxBase, 3); c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); c->funcs->push_transform (c->data, scaleX.to_float (c->instancer (varIdxBase, 0)), 0., 0., scaleY.to_float (c->instancer (varIdxBase, 1)), 0., 0.); c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); c->recurse (this+src); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float s = scale + c->instancer (varIdxBase, 0); c->funcs->push_transform (c->data, s, 0., 0., s, 0., 0.); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float s = scale + c->instancer (varIdxBase, 0); float tCenterX = centerX + c->instancer (varIdxBase, 1); float tCenterY = centerY + c->instancer (varIdxBase, 2); c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); c->funcs->push_transform (c->data, s, 0., 0., s, 0., 0.); c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); c->recurse (this+src); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float a = angle.to_float (c->instancer (varIdxBase, 0)); float cc = cosf (a * (float) M_PI); float ss = sinf (a * (float) M_PI); c->funcs->push_transform (c->data, cc, ss, -ss, cc, 0., 0.); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float a = angle.to_float (c->instancer (varIdxBase, 0)); float cc = cosf (a * (float) M_PI); float ss = sinf (a * (float) M_PI); float tCenterX = centerX + c->instancer (varIdxBase, 1); float tCenterY = centerY + c->instancer (varIdxBase, 2); c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); c->funcs->push_transform (c->data, cc, ss, -ss, cc, 0., 0.); c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); c->recurse (this+src); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float x = +tanf (xSkewAngle.to_float(c->instancer (varIdxBase, 0)) * (float) M_PI); float y = -tanf (ySkewAngle.to_float(c->instancer (varIdxBase, 1)) * (float) M_PI); c->funcs->push_transform (c->data, 1., y, x, 1., 0., 0.); c->recurse (this+src); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); return_trace (out->src.serialize_subset (c, src, this)); } 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 { float x = +tanf (xSkewAngle.to_float(c->instancer (varIdxBase, 0)) * (float) M_PI); float y = -tanf (ySkewAngle.to_float(c->instancer (varIdxBase, 1)) * (float) M_PI); float tCenterX = centerX + c->instancer (varIdxBase, 2); float tCenterY = centerY + c->instancer (varIdxBase, 3); c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); c->funcs->push_transform (c->data, 1., y, x, 1., 0., 0.); c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); c->recurse (this+src); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ Offset24To 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 { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); if (!out->src.serialize_subset (c, src, this)) return_trace (false); return_trace (out->backdrop.serialize_subset (c, backdrop, this)); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && src.sanitize (c, this) && backdrop.sanitize (c, this)); } void paint_glyph (hb_paint_context_t *c) const { 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 src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ Offset24To 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 VarStoreInstancer &instancer HB_UNUSED) const { clip_box.xMin = xMin; clip_box.yMin = yMin; clip_box.xMax = xMax; clip_box.yMax = yMax; } 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 { void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const { value.get_clip_box(clip_box, instancer); if (instancer) { clip_box.xMin += _hb_roundf (instancer (varIdxBase, 0)); clip_box.yMin += _hb_roundf (instancer (varIdxBase, 1)); clip_box.xMax += _hb_roundf (instancer (varIdxBase, 2)); clip_box.yMax += _hb_roundf (instancer (varIdxBase, 3)); } } }; struct ClipBox { ClipBox* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); switch (u.format) { case 1: return_trace (reinterpret_cast (c->embed (u.format1))); case 2: return_trace (reinterpret_cast (c->embed (u.format2))); default:return_trace (nullptr); } } template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } } bool get_extents (hb_glyph_extents_t *extents, const VarStoreInstancer &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; } ClipRecord* copy (hb_serialize_context_t *c, const void *base) const { TRACE_SERIALIZE (this); auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); if (!out->clipBox.serialize_copy (c, clipBox, base)) return_trace (nullptr); return_trace (out); } 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 VarStoreInstancer &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; // Box or VarBox public: DEFINE_SIZE_STATIC (7); }; DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); struct ClipList { unsigned serialize_clip_records (hb_serialize_context_t *c, 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 (!c->copy (record, this)) 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 (!c->copy (record, this)) return_trace (0); count++; } return_trace (count); } bool subset (hb_subset_context_t *c) 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->serializer, 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 VarStoreInstancer &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 clips; // Clip records, sorted by startGlyphID public: DEFINE_SIZE_ARRAY_SIZED (5, clips); }; struct Paint { template bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!c->check_start_recursion (HB_COLRV1_MAX_NESTING_LEVEL))) return_trace (c->no_dispatch_return_value ()); return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...))); } template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); case 4: return_trace (c->dispatch (u.paintformat4, std::forward (ds)...)); case 5: return_trace (c->dispatch (u.paintformat5, std::forward (ds)...)); case 6: return_trace (c->dispatch (u.paintformat6, std::forward (ds)...)); case 7: return_trace (c->dispatch (u.paintformat7, std::forward (ds)...)); case 8: return_trace (c->dispatch (u.paintformat8, std::forward (ds)...)); case 9: return_trace (c->dispatch (u.paintformat9, std::forward (ds)...)); case 10: return_trace (c->dispatch (u.paintformat10, std::forward (ds)...)); case 11: return_trace (c->dispatch (u.paintformat11, std::forward (ds)...)); case 12: return_trace (c->dispatch (u.paintformat12, std::forward (ds)...)); case 13: return_trace (c->dispatch (u.paintformat13, std::forward (ds)...)); case 14: return_trace (c->dispatch (u.paintformat14, std::forward (ds)...)); case 15: return_trace (c->dispatch (u.paintformat15, std::forward (ds)...)); case 16: return_trace (c->dispatch (u.paintformat16, std::forward (ds)...)); case 17: return_trace (c->dispatch (u.paintformat17, std::forward (ds)...)); case 18: return_trace (c->dispatch (u.paintformat18, std::forward (ds)...)); case 19: return_trace (c->dispatch (u.paintformat19, std::forward (ds)...)); case 20: return_trace (c->dispatch (u.paintformat20, std::forward (ds)...)); case 21: return_trace (c->dispatch (u.paintformat21, std::forward (ds)...)); case 22: return_trace (c->dispatch (u.paintformat22, std::forward (ds)...)); case 23: return_trace (c->dispatch (u.paintformat23, std::forward (ds)...)); case 24: return_trace (c->dispatch (u.paintformat24, std::forward (ds)...)); case 25: return_trace (c->dispatch (u.paintformat25, std::forward (ds)...)); case 26: return_trace (c->dispatch (u.paintformat26, std::forward (ds)...)); case 27: return_trace (c->dispatch (u.paintformat27, std::forward (ds)...)); case 28: return_trace (c->dispatch (u.paintformat28, std::forward (ds)...)); case 29: return_trace (c->dispatch (u.paintformat29, std::forward (ds)...)); case 30: return_trace (c->dispatch (u.paintformat30, std::forward (ds)...)); case 31: return_trace (c->dispatch (u.paintformat31, std::forward (ds)...)); case 32: return_trace (c->dispatch (u.paintformat32, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } } protected: union { HBUINT8 format; PaintColrLayers paintformat1; NoVariable paintformat2; Variable paintformat3; NoVariable> paintformat4; Variable> paintformat5; NoVariable> paintformat6; Variable> paintformat7; NoVariable> paintformat8; Variable> paintformat9; PaintGlyph paintformat10; PaintColrGlyph paintformat11; PaintTransform paintformat12; PaintTransform paintformat13; NoVariable paintformat14; Variable paintformat15; NoVariable paintformat16; Variable paintformat17; NoVariable paintformat18; Variable paintformat19; NoVariable paintformat20; Variable paintformat21; NoVariable paintformat22; Variable paintformat23; NoVariable paintformat24; Variable paintformat25; NoVariable paintformat26; Variable paintformat27; NoVariable paintformat28; Variable paintformat29; NoVariable paintformat30; Variable 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 { 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)); } 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; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint, * Typically PaintColrLayers */ public: DEFINE_SIZE_STATIC (6); }; struct BaseGlyphList : SortedArray32Of { bool subset (hb_subset_context_t *c) 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)) 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::sanitize (c, this)); } }; struct LayerList : Array32OfOffset32To { const Paint& get_paint (unsigned i) const { return this+(*this)[i]; } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); if (unlikely (!c->serializer->extend_min (out))) return_trace (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) || !o->serialize_subset (c, _.second, this)) return_trace (false); } return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (Array32OfOffset32To::sanitize (c, this)); } }; struct COLR { static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; bool has_v0_data () const { return numBaseGlyphs; } bool has_v1_data () const { return (this+baseGlyphList).len; } 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 all_layers = (this+layersZ).as_array (numLayers); hb_array_t 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 (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) const { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } private: hb_blob_ptr_t 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 baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); hb_array_t all_layers = (this+layersZ).as_array (numLayers); for (const BaseGlyphRecord record : baseGlyphs) { if (!glyphs->has (record.glyphId)) continue; hb_array_t 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) const { if (version != 1) return; hb_set_t visited_glyphs; hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_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 LayerList& get_layerList () const { return (this+layerList); } const BaseGlyphList& get_baseglyphList () const { return (this+baseGlyphList); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && (this+layersZ).sanitize (c, numLayers) && (version == 0 || (version == 1 && baseGlyphList.sanitize (c, this) && layerList.sanitize (c, this) && clipList.sanitize (c, this) && varIdxMap.sanitize (c, this) && varStore.sanitize (c, this)))); } template 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 _ : + 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& _ : + 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 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 (false, Null (BaseGlyphRecord)); BaseGlyphRecord new_record = {}; new_record.glyphId = new_gid; new_record.numLayers = old_record->numLayers; return hb_pair_t (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 out_layers; if (unlikely (!old_record || old_record->firstLayerIdx >= numLayers || old_record->firstLayerIdx + old_record->numLayers > numLayers)) return hb_pair_t> (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> (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> (true, out_layers); }) | hb_filter (hb_first) | hb_map_retains_sorting (hb_second) ; if (version == 0 && (!base_it || !layer_it)) return_trace (false); COLR *colr_prime = c->serializer->start_embed (); if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false); if (version == 0) return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); auto snap = c->serializer->snapshot (); if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false); if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this)) { if (c->serializer->in_error ()) return_trace (false); //no more COLRv1 glyphs: downgrade to version 0 c->serializer->revert (snap); return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); } if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); colr_prime->layerList.serialize_subset (c, layerList, this); colr_prime->clipList.serialize_subset (c, clipList, this); colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); //TODO: subset varStore once it's implemented in fonttools return_trace (true); } 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; } bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { if (version != 1) return false; VarStoreInstancer instancer (this+varStore, this+varIdxMap, hb_array (font->coords, font->num_coords)); if ((this+clipList).get_extents (glyph, extents, instancer)) { font->scale_glyph_extents (extents); return true; } return false; } bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const { VarStoreInstancer instancer (this+varStore, this+varIdxMap, hb_array (font->coords, font->num_coords)); hb_paint_context_t c (this, funcs, data, instancer); const Paint *paint = get_base_glyph_paint (glyph); if (paint) { // COLRv1 glyph c.funcs->push_root_transform (data, font); c.recurse (*paint); c.funcs->pop_root_transform (c.data); return true; } const BaseGlyphRecord *record = get_base_glyph_record (glyph); if (record && ((hb_codepoint_t) record->glyphId == glyph)) { // COLRv0 glyph funcs->push_root_transform (data, font); for (const auto &r : (this+layersZ).as_array (numLayers) .sub_array (record->firstLayerIdx, record->numLayers)) { c.funcs->push_clip_glyph (c.data, r.glyphId); c.funcs->color (c.data, r.colorIdx, 1.); c.funcs->pop_clip (c.data); } c.funcs->pop_root_transform (c.data); return true; } return false; } protected: HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ NNOffset32To> baseGlyphsZ; /* Offset to Base Glyph records. */ NNOffset32To> layersZ; /* Offset to Layer Records. */ HBUINT16 numLayers; /* Number of Layer Records. */ // Version-1 additions Offset32To baseGlyphList; Offset32To layerList; Offset32To clipList; // Offset to ClipList table (may be NULL) Offset32To varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) Offset32To 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) { depth_left--; if (depth_left > 0) paint.dispatch (this); depth_left++; } } /* namespace OT */ #endif /* HB_OT_COLOR_COLR_TABLE_HH */