Edit

kc3-lang/harfbuzz/src/hb-ot-stat-table.hh

Branch :

  • Show log

    Commit

  • Author : Behdad Esfahbod
    Date : 2024-09-22 13:19:02
    Hash : 7a890c2e
    Message : Add hb_barrier() to switches of unions https://github.com/harfbuzz/harfbuzz/pull/4864#issuecomment-2366923736

  • src/hb-ot-stat-table.hh
  • /*
     * Copyright © 2018  Ebrahim Byagowi
     *
     *  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.
     */
    
    #ifndef HB_OT_STAT_TABLE_HH
    #define HB_OT_STAT_TABLE_HH
    
    #include "hb-open-type.hh"
    #include "hb-ot-layout-common.hh"
    
    /*
     * STAT -- Style Attributes
     * https://docs.microsoft.com/en-us/typography/opentype/spec/stat
     */
    #define HB_OT_TAG_STAT HB_TAG('S','T','A','T')
    
    
    namespace OT {
    
    enum
    {
      OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001,	/* If set, this axis value table
    						 * provides axis value information
    						 * that is applicable to other fonts
    						 * within the same font family. This
    						 * is used if the other fonts were
    						 * released earlier and did not include
    						 * information about values for some axis.
    						 * If newer versions of the other
    						 * fonts include the information
    						 * themselves and are present,
    						 * then this record is ignored. */
      ELIDABLE_AXIS_VALUE_NAME = 0x0002		/* If set, it indicates that the axis
    						 * value represents the “normal” value
    						 * for the axis and may be omitted when
    						 * composing name strings. */
      // Reserved = 0xFFFC				/* Reserved for future use — set to zero. */
    };
    
    static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_value,
                                                  const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location)
    {
      if (!user_axes_location->has (axis_tag))
        return false;
    
      double axis_value_double = static_cast<double>(axis_value);
      Triple axis_range = user_axes_location->get (axis_tag);
      return (axis_value_double < axis_range.minimum || axis_value_double > axis_range.maximum);
    }
    
    struct StatAxisRecord
    {
      int cmp (hb_tag_t key) const { return tag.cmp (key); }
    
      hb_ot_name_id_t get_name_id () const { return nameID; }
    
      hb_tag_t get_axis_tag () const { return tag; }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (likely (c->check_struct (this)));
      }
    
      protected:
      Tag		tag;		/* A tag identifying the axis of design variation. */
      NameID	nameID;		/* The name ID for entries in the 'name' table that
    				 * provide a display string for this axis. */
      HBUINT16	ordering;	/* A value that applications can use to determine
    				 * primary sorting of face names, or for ordering
    				 * of descriptors when composing family or face names. */
      public:
      DEFINE_SIZE_STATIC (8);
    };
    
    struct AxisValueFormat1
    {
      unsigned int get_axis_index () const { return axisIndex; }
      float get_value ()             const { return value.to_float (); }
    
      hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
    
      hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
      {
        unsigned axis_idx = get_axis_index ();
        return axis_records[axis_idx].get_axis_tag ();
      }
    
      bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
                            const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
      {
        hb_tag_t axis_tag = get_axis_tag (axis_records);
        float axis_value = get_value ();
    
        return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
      }
    
      bool subset (hb_subset_context_t *c,
                   const hb_array_t<const StatAxisRecord> axis_records) const
      {
        TRACE_SUBSET (this);
        const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
    
        if (keep_axis_value (axis_records, user_axes_location))
          return_trace (c->serializer->embed (this));
    
        return_trace (false);
      }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (c->check_struct (this));
      }
    
      protected:
      HBUINT16	format;		/* Format identifier — set to 1. */
      HBUINT16	axisIndex;	/* Zero-base index into the axis record array
    				 * identifying the axis of design variation
    				 * to which the axis value record applies.
    				 * Must be less than designAxisCount. */
      HBUINT16	flags;		/* Flags — see below for details. */
      NameID	valueNameID;	/* The name ID for entries in the 'name' table
    				 * that provide a display string for this
    				 * attribute value. */
      F16DOT16	value;		/* A numeric value for this attribute value. */
      public:
      DEFINE_SIZE_STATIC (12);
    };
    
    struct AxisValueFormat2
    {
      unsigned int get_axis_index () const { return axisIndex; }
      float get_value ()             const { return nominalValue.to_float (); }
    
      hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
    
      hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
      {
        unsigned axis_idx = get_axis_index ();
        return axis_records[axis_idx].get_axis_tag ();
      }
    
      bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
                            const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
      {
        hb_tag_t axis_tag = get_axis_tag (axis_records);
        float axis_value = get_value ();
    
        return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
      }
    
      bool subset (hb_subset_context_t *c,
                   const hb_array_t<const StatAxisRecord> axis_records) const
      {
        TRACE_SUBSET (this);
        const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
    
        if (keep_axis_value (axis_records, user_axes_location))
          return_trace (c->serializer->embed (this));
    
        return_trace (false);
      }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (c->check_struct (this));
      }
    
      protected:
      HBUINT16	format;		/* Format identifier — set to 2. */
      HBUINT16	axisIndex;	/* Zero-base index into the axis record array
    				 * identifying the axis of design variation
    				 * to which the axis value record applies.
    				 * Must be less than designAxisCount. */
      HBUINT16	flags;		/* Flags — see below for details. */
      NameID	valueNameID;	/* The name ID for entries in the 'name' table
    				 * that provide a display string for this
    				 * attribute value. */
      F16DOT16	nominalValue;	/* A numeric value for this attribute value. */
      F16DOT16	rangeMinValue;	/* The minimum value for a range associated
    				 * with the specified name ID. */
      F16DOT16	rangeMaxValue;	/* The maximum value for a range associated
    				 * with the specified name ID. */
      public:
      DEFINE_SIZE_STATIC (20);
    };
    
    struct AxisValueFormat3
    {
      unsigned int get_axis_index () const { return axisIndex; }
      float get_value ()             const { return value.to_float (); }
    
      hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
    
      hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const
      {
        unsigned axis_idx = get_axis_index ();
        return axis_records[axis_idx].get_axis_tag ();
      }
    
      bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
                            const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
      {
        hb_tag_t axis_tag = get_axis_tag (axis_records);
        float axis_value = get_value ();
    
        return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
      }
    
      bool subset (hb_subset_context_t *c,
                   const hb_array_t<const StatAxisRecord> axis_records) const
      {
        TRACE_SUBSET (this);
        const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
    
        if (keep_axis_value (axis_records, user_axes_location))
          return_trace (c->serializer->embed (this));
    
        return_trace (false);
      }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (c->check_struct (this));
      }
    
      protected:
      HBUINT16	format;		/* Format identifier — set to 3. */
      HBUINT16	axisIndex;	/* Zero-base index into the axis record array
    				 * identifying the axis of design variation
    				 * to which the axis value record applies.
    				 * Must be less than designAxisCount. */
      HBUINT16	flags;		/* Flags — see below for details. */
      NameID	valueNameID;	/* The name ID for entries in the 'name' table
    				 * that provide a display string for this
    				 * attribute value. */
      F16DOT16	value;		/* A numeric value for this attribute value. */
      F16DOT16	linkedValue;	/* The numeric value for a style-linked mapping
    				 * from this value. */
      public:
      DEFINE_SIZE_STATIC (16);
    };
    
    struct AxisValueRecord
    {
      unsigned int get_axis_index () const { return axisIndex; }
      float get_value ()             const { return value.to_float (); }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (c->check_struct (this));
      }
    
      protected:
      HBUINT16	axisIndex;	/* Zero-base index into the axis record array
    				 * identifying the axis to which this value
    				 * applies. Must be less than designAxisCount. */
      F16DOT16	value;		/* A numeric value for this attribute value. */
      public:
      DEFINE_SIZE_STATIC (6);
    };
    
    struct AxisValueFormat4
    {
      const AxisValueRecord &get_axis_record (unsigned int axis_index) const
      { return axisValues.as_array (axisCount)[axis_index]; }
    
      bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
                            const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
      {
        hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount);
    
        for (const auto& rec : axis_value_records)
        {
          unsigned axis_idx = rec.get_axis_index ();
          float axis_value = rec.get_value ();
          hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag ();
    
          if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location))
            return false;
        }
    
        return true;
      }
    
      bool subset (hb_subset_context_t *c,
                   const hb_array_t<const StatAxisRecord> axis_records) const
      {
        TRACE_SUBSET (this);
        const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location;
        if (!keep_axis_value (axis_records, user_axes_location))
          return_trace (false);
    
        unsigned total_size = min_size + axisCount * AxisValueRecord::static_size;
        auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size);
        if (unlikely (!out)) return_trace (false);
        hb_memcpy (out, this, total_size);
        return_trace (true);
      }
    
      hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (likely (c->check_struct (this) &&
    			  hb_barrier () &&
                              axisValues.sanitize (c, axisCount)));
      }
    
      protected:
      HBUINT16	format;		/* Format identifier — set to 4. */
      HBUINT16	axisCount;	/* The total number of axes contributing to
    				 * this axis-values combination. */
      HBUINT16	flags;		/* Flags — see below for details. */
      NameID	valueNameID;	/* The name ID for entries in the 'name' table
    				 * that provide a display string for this
    				 * attribute value. */
      UnsizedArrayOf<AxisValueRecord>
    		axisValues;	/* Array of AxisValue records that provide the
    				 * combination of axis values, one for each
    				 * contributing axis. */
      public:
      DEFINE_SIZE_ARRAY (8, axisValues);
    };
    
    struct AxisValue
    {
      float get_value (unsigned int axis_index) const
      {
        switch (u.format)
        {
        case 1: hb_barrier (); return u.format1.get_value ();
        case 2: hb_barrier (); return u.format2.get_value ();
        case 3: hb_barrier (); return u.format3.get_value ();
        case 4: hb_barrier (); return u.format4.get_axis_record (axis_index).get_value ();
        default:return 0.f;
        }
      }
    
      unsigned int get_axis_index () const
      {
        switch (u.format)
        {
        case 1: hb_barrier (); return u.format1.get_axis_index ();
        case 2: hb_barrier (); return u.format2.get_axis_index ();
        case 3: hb_barrier (); return u.format3.get_axis_index ();
        /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */
        default:return -1;
        }
      }
    
      hb_ot_name_id_t get_value_name_id () const
      {
        switch (u.format)
        {
        case 1: hb_barrier (); return u.format1.get_value_name_id ();
        case 2: hb_barrier (); return u.format2.get_value_name_id ();
        case 3: hb_barrier (); return u.format3.get_value_name_id ();
        case 4: hb_barrier (); return u.format4.get_value_name_id ();
        default:return HB_OT_NAME_ID_INVALID;
        }
      }
    
      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: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
        case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
        case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
        case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
        default:return_trace (c->default_return_value ());
        }
      }
    
      bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
                            hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
      {
        switch (u.format)
        {
        case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location);
        case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location);
        case 3: hb_barrier (); return u.format3.keep_axis_value (axis_records, user_axes_location);
        case 4: hb_barrier (); return u.format4.keep_axis_value (axis_records, user_axes_location);
        default:return false;
        }
      }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        if (unlikely (!c->check_struct (this)))
          return_trace (false);
        hb_barrier ();
    
        switch (u.format)
        {
        case 1: hb_barrier (); return_trace (u.format1.sanitize (c));
        case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
        case 3: hb_barrier (); return_trace (u.format3.sanitize (c));
        case 4: hb_barrier (); return_trace (u.format4.sanitize (c));
        default:return_trace (true);
        }
      }
    
      protected:
      union
      {
      HBUINT16		format;
      AxisValueFormat1	format1;
      AxisValueFormat2	format2;
      AxisValueFormat3	format3;
      AxisValueFormat4	format4;
      } u;
      public:
      DEFINE_SIZE_UNION (2, format);
    };
    
    struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>>
    {
      bool subset (hb_subset_context_t *c,
                   unsigned axisValueCount,
                   unsigned& count,
                   const hb_array_t<const StatAxisRecord> axis_records) const
      {
        TRACE_SUBSET (this);
    
        auto axisValueOffsets = as_array (axisValueCount);
        count = 0;
        for (const auto& offset : axisValueOffsets)
        {
          if (!offset) continue;
          auto o_snap = c->serializer->snapshot ();
          auto *o = c->serializer->embed (offset);
          if (!o) return_trace (false);
          if (!o->serialize_subset (c, offset, this, axis_records))
          {
            c->serializer->revert (o_snap);
            continue;
          }
          count++;
        }
    
        return_trace (count);
      }
    };
    
    struct STAT
    {
      static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT;
    
      bool has_data () const { return version.to_int (); }
    
      bool get_value (hb_tag_t tag, float *value) const
      {
        unsigned int axis_index;
        if (!get_design_axes ().lfind (tag, &axis_index)) return false;
    
        hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets ();
        for (unsigned int i = 0; i < axis_values.length; i++)
        {
          const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i];
          if (axis_value.get_axis_index () == axis_index)
          {
    	if (value)
    	  *value = axis_value.get_value (axis_index);
    	return true;
          }
        }
        return false;
      }
    
      unsigned get_design_axis_count () const { return designAxisCount; }
    
      hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const
      {
        if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID;
        const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index];
        return axis_record.get_name_id ();
      }
    
      unsigned get_axis_value_count () const { return axisValueCount; }
    
      hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const
      {
        if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID;
        const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]);
        return axis_value.get_value_name_id ();
      }
    
      void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
                             hb_set_t *nameids_to_retain /* OUT */) const
      {
        if (!has_data ()) return;
    
        + get_design_axes ()
        | hb_map (&StatAxisRecord::get_name_id)
        | hb_sink (nameids_to_retain)
        ;
    
        auto designAxes = get_design_axes ();
    
        + get_axis_value_offsets ()
        | hb_map (hb_add (&(this + offsetToAxisValueOffsets)))
        | hb_filter ([&] (const AxisValue& _)
                     { return _.keep_axis_value (designAxes, user_axes_location); })
        | hb_map (&AxisValue::get_value_name_id)
        | hb_sink (nameids_to_retain)
        ;
    
        nameids_to_retain->add (elidedFallbackNameID);
      }
    
      bool subset (hb_subset_context_t *c) const
      {
        TRACE_SUBSET (this);
        STAT *out = c->serializer->embed (this);
        if (unlikely (!out)) return_trace (false);
    
        auto designAxes = get_design_axes ();
        for (unsigned i = 0; i < (unsigned)designAxisCount; i++)
          if (unlikely (!c->serializer->embed (designAxes[i])))
              return_trace (false);
    
        if (designAxisCount)
          c->serializer->check_assign (out->designAxesOffset, this->get_size (),
                                       HB_SERIALIZE_ERROR_INT_OVERFLOW);
    
        unsigned count = 0;
        out->offsetToAxisValueOffsets.serialize_subset (c, offsetToAxisValueOffsets, this,
                                                        axisValueCount, count, designAxes);
        return_trace (c->serializer->check_assign (out->axisValueCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
      }
    
      bool sanitize (hb_sanitize_context_t *c) const
      {
        TRACE_SANITIZE (this);
        return_trace (likely (c->check_struct (this) &&
    			  hb_barrier () &&
    			  version.major == 1 &&
    			  version.minor > 0 &&
    			  designAxesOffset.sanitize (c, this, designAxisCount) &&
    			  offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets))));
      }
    
      protected:
      hb_array_t<const StatAxisRecord> const get_design_axes () const
      { return (this+designAxesOffset).as_array (designAxisCount); }
    
      hb_array_t<const Offset16To<AxisValue>> const get_axis_value_offsets () const
      { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); }
    
    
      protected:
      FixedVersion<>version;	/* Version of the stat table
    				 * initially set to 0x00010002u */
      HBUINT16	designAxisSize;	/* The size in bytes of each axis record. */
      HBUINT16	designAxisCount;/* The number of design axis records. In a
    				 * font with an 'fvar' table, this value must be
    				 * greater than or equal to the axisCount value
    				 * in the 'fvar' table. In all fonts, must
    				 * be greater than zero if axisValueCount
    				 * is greater than zero. */
      NNOffset32To<UnsizedArrayOf<StatAxisRecord>>
    		designAxesOffset;
    				/* Offset in bytes from the beginning of
    				 * the STAT table to the start of the design
    				 * axes array. If designAxisCount is zero,
    				 * set to zero; if designAxisCount is greater
    				 * than zero, must be greater than zero. */
      HBUINT16	axisValueCount;	/* The number of axis value tables. */
      NNOffset32To<AxisValueOffsetArray>
    		offsetToAxisValueOffsets;
    				/* Offset in bytes from the beginning of
    				 * the STAT table to the start of the design
    				 * axes value offsets array. If axisValueCount
    				 * is zero, set to zero; if axisValueCount is
    				 * greater than zero, must be greater than zero. */
      NameID	elidedFallbackNameID;
    				/* Name ID used as fallback when projection of
    				 * names into a particular font model produces
    				 * a subfamily name containing only elidable
    				 * elements. */
      public:
      DEFINE_SIZE_STATIC (20);
    };
    
    
    } /* namespace OT */
    
    
    #endif /* HB_OT_STAT_TABLE_HH */