| /* |
| * Copyright © 2018 Ebrahim Byagowi |
| * Copyright © 2020 Google, Inc. |
| * |
| * This is part of HarfBuzz, a text shaping library. |
| * |
| * Permission is hereby granted, without written agreement and without |
| * license or royalty fees, to use, copy, modify, and distribute this |
| * software and its documentation for any purpose, provided that the |
| * above copyright notice and the following two paragraphs appear in |
| * all copies of this software. |
| * |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| * |
| * Google Author(s): Calder Kitagawa |
| */ |
| |
| #ifndef OT_COLOR_COLR_COLR_HH |
| #define OT_COLOR_COLR_COLR_HH |
| |
| #include "../../../hb.hh" |
| #include "../../../hb-open-type.hh" |
| #include "../../../hb-ot-var-common.hh" |
| #include "../../../hb-paint.hh" |
| #include "../../../hb-paint-extents.hh" |
| |
| /* |
| * COLR -- Color |
| * https://docs.microsoft.com/en-us/typography/opentype/spec/colr |
| */ |
| #define HB_OT_TAG_COLR HB_TAG('C','O','L','R') |
| |
| namespace OT { |
| struct hb_paint_context_t; |
| } |
| |
| namespace OT { |
| |
| struct COLR; |
| |
| struct Paint; |
| |
| struct hb_paint_context_t : |
| hb_dispatch_context_t<hb_paint_context_t> |
| { |
| const char *get_name () { return "PAINT"; } |
| template <typename T> |
| return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); } |
| static return_t default_return_value () { return hb_empty_t (); } |
| |
| const COLR* get_colr_table () const |
| { return reinterpret_cast<const COLR *> (base); } |
| |
| public: |
| const void *base; |
| hb_paint_funcs_t *funcs; |
| void *data; |
| hb_font_t *font; |
| unsigned int palette_index; |
| hb_color_t foreground; |
| VarStoreInstancer &instancer; |
| int depth_left = HB_MAX_NESTING_LEVEL; |
| int edge_count = HB_COLRV1_MAX_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_, |
| VarStoreInstancer &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); } |
| |
| 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_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 <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 |
| { value.closurev1 (c); } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &instancer) const |
| { |
| TRACE_SUBSET (this); |
| if (!value.subset (c, instancer, varIdxBase)) return_trace (false); |
| if (c->plan->all_axes_pinned) |
| return_trace (true); |
| |
| //TODO: update varIdxBase for partial-instancing |
| 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 |
| { |
| TRACE_PAINT (this); |
| value.paint_glyph (c, varIdxBase); |
| } |
| |
| void get_color_stop (hb_paint_context_t *c, |
| hb_color_stop_t *stop, |
| const VarStoreInstancer &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_STATIC (4 + T::static_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 VarStoreInstancer &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 VarStoreInstancer &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_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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 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 (c, &color_stops[i], instancer); |
| *count = i; |
| } |
| |
| return len; |
| } |
| |
| HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line, |
| void *color_line_data, |
| unsigned int start, |
| unsigned int *count, |
| hb_color_stop_t *color_stops, |
| void *user_data) |
| { |
| const ColorLine *thiz = (const ColorLine *) color_line_data; |
| hb_paint_context_t *c = (hb_paint_context_t *) user_data; |
| return thiz->get_color_stops (c, start, count, color_stops, c->instancer); |
| } |
| |
| hb_paint_extend_t get_extend () const |
| { |
| return (hb_paint_extend_t) (unsigned int) extend; |
| } |
| |
| HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line, |
| void *color_line_data, |
| void *user_data) |
| { |
| const ColorLine *thiz = (const ColorLine *) color_line_data; |
| return thiz->get_extend (); |
| } |
| |
| Extend extend; |
| Array16Of<Var<ColorStop>> stops; |
| public: |
| DEFINE_SIZE_ARRAY_SIZED (3, stops); |
| }; |
| |
| // Composition modes |
| |
| // Compositing modes are taken from https://www.w3.org/TR/compositing-1/ |
| // NOTE: a brief audit of major implementations suggests most support most |
| // or all of the specified modes. |
| struct CompositeMode : HBUINT8 |
| { |
| enum { |
| // Porter-Duff modes |
| // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators |
| COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear |
| COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src |
| COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst |
| COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover |
| COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover |
| COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin |
| COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin |
| COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout |
| COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout |
| COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop |
| COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop |
| COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor |
| COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus |
| |
| // Blend modes |
| // https://www.w3.org/TR/compositing-1/#blending |
| COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen |
| COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay |
| COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken |
| COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten |
| COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge |
| COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn |
| COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight |
| COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight |
| COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference |
| COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion |
| COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply |
| |
| // Modes that, uniquely, do not operate on components |
| // https://www.w3.org/TR/compositing-1/#blendingnonseparable |
| COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue |
| COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation |
| COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor |
| COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity |
| }; |
| public: |
| DEFINE_SIZE_STATIC (1); |
| }; |
| |
| struct Affine2x3 |
| { |
| bool sanitize (hb_sanitize_context_t *c) const |
| { |
| TRACE_SANITIZE (this); |
| return_trace (c->check_struct (this)); |
| } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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 VarStoreInstancer &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); } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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); } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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); } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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); } |
| |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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)); |
| } |
| } |
| }; |
| |
| struct ClipBox |
| { |
| bool subset (hb_subset_context_t *c, |
| const VarStoreInstancer &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 ()); |
| } |
| } |
| |
| 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 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; } |
| |
| bool subset (hb_subset_context_t *c, |
| const void *base, |
| const VarStoreInstancer &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 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> 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 VarStoreInstancer &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 VarStoreInstancer &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 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<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 VarStoreInstancer &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 VarStoreInstancer &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 VarStoreInstancer &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 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 (this+baseGlyphList).len > 0; |
| |
| return false; |
| } |
| |
| 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) const |
| { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } |
| |
| 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) 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<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 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) |
| return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); |
| |
| auto snap = c->serializer->snapshot (); |
| if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false); |
| |
| VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, |
| varIdxMap ? &(this+varIdxMap) : nullptr, |
| c->plan->normalized_coords.as_array ()); |
| |
| if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer)) |
| { |
| 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, instancer); |
| colr_prime->clipList.serialize_subset (c, clipList, this, instancer); |
| if (!varStore || c->plan->all_axes_pinned) |
| return_trace (true); |
| |
| colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); |
| colr_prime->varStore.serialize_copy (c->serializer, varStore, this); |
| 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; |
| } |
| |
| #ifndef HB_NO_PAINT |
| 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 (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) |
| { |
| 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 VarStoreInstancer instancer) const |
| { |
| return (this+clipList).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 |
| { |
| VarStoreInstancer instancer (&(this+varStore), |
| &(this+varIdxMap), |
| hb_array (font->coords, font->num_coords)); |
| hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); |
| |
| if (version == 1) |
| { |
| const Paint *paint = get_base_glyph_paint (glyph); |
| if (paint) |
| { |
| // COLRv1 glyph |
| |
| VarStoreInstancer instancer (&(this+varStore), |
| &(this+varIdxMap), |
| hb_array (font->coords, font->num_coords)); |
| |
| 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<VariationStore> 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++) |
| { |
| 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); |
| } |
| } |
| |
| void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const |
| { |
| TRACE_PAINT (this); |
| |
| 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); |
| return; |
| } |
| c->funcs->pop_transform (c->data); |
| |
| const COLR *colr_table = c->get_colr_table (); |
| const Paint *paint = colr_table->get_base_glyph_paint (gid); |
| |
| hb_glyph_extents_t extents = {0}; |
| bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer); |
| |
| if (has_clip_box) |
| c->funcs->push_clip_rectangle (c->data, |
| extents.x_bearing, |
| extents.y_bearing + extents.height, |
| extents.x_bearing + extents.width, |
| extents.y_bearing); |
| |
| if (paint) |
| c->recurse (*paint); |
| |
| if (has_clip_box) |
| c->funcs->pop_clip (c->data); |
| } |
| |
| } /* namespace OT */ |
| |
| #endif /* OT_COLOR_COLR_COLR_HH */ |