Coverage Report

Created: 2025-11-09 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
2
#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
3
4
#include "PairSet.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GPOS_impl {
9
10
11
template <typename Types>
12
struct PairPosFormat1_3
13
{
14
  using PairSet = GPOS_impl::PairSet<Types>;
15
  using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
16
17
  protected:
18
  HBUINT16      format;                 /* Format identifier--format = 1 */
19
  typename Types::template OffsetTo<Coverage>
20
                coverage;               /* Offset to Coverage table--from
21
                                         * beginning of subtable */
22
  ValueFormat   valueFormat[2];         /* [0] Defines the types of data in
23
                                         * ValueRecord1--for the first glyph
24
                                         * in the pair--may be zero (0) */
25
                                        /* [1] Defines the types of data in
26
                                         * ValueRecord2--for the second glyph
27
                                         * in the pair--may be zero (0) */
28
  Array16Of<typename Types::template OffsetTo<PairSet>>
29
                pairSet;                /* Array of PairSet tables
30
                                         * ordered by Coverage Index */
31
  public:
32
  DEFINE_SIZE_ARRAY (8 + Types::size, pairSet);
33
34
  bool sanitize (hb_sanitize_context_t *c) const
35
0
  {
36
0
    TRACE_SANITIZE (this);
37
38
0
    if (!c->check_struct (this)) return_trace (false);
39
0
    hb_barrier ();
40
41
0
    unsigned int len1 = valueFormat[0].get_len ();
42
0
    unsigned int len2 = valueFormat[1].get_len ();
43
0
    typename PairSet::sanitize_closure_t closure =
44
0
    {
45
0
      valueFormat,
46
0
      len1,
47
0
      PairSet::get_size (len1, len2)
48
0
    };
49
50
0
    return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
51
0
  }
52
53
  bool intersects (const hb_set_t *glyphs) const
54
0
  {
55
0
    auto &cov = this+coverage;
56
0
57
0
    if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len))
58
0
    {
59
0
      for (hb_codepoint_t g : glyphs->iter())
60
0
      {
61
0
  unsigned i = cov.get_coverage (g);
62
0
  if ((this+pairSet[i]).intersects (glyphs, valueFormat))
63
0
    return true;
64
0
      }
65
0
      return false;
66
0
    }
67
0
68
0
    return
69
0
    + hb_zip (cov, pairSet)
70
0
    | hb_filter (*glyphs, hb_first)
71
0
    | hb_map (hb_second)
72
0
    | hb_map ([glyphs, this] (const typename Types::template OffsetTo<PairSet> &_)
73
0
              { return (this+_).intersects (glyphs, valueFormat); })
74
0
    | hb_any
75
0
    ;
76
0
  }
77
78
0
  void closure_lookups (hb_closure_lookups_context_t *c) const {}
79
  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
80
0
  {
81
0
    if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return;
82
0
83
0
    auto it =
84
0
    + hb_zip (this+coverage, pairSet)
85
0
    | hb_filter (c->glyph_set, hb_first)
86
0
    | hb_map (hb_second)
87
0
    ;
88
0
89
0
    if (!it) return;
90
0
    + it
91
0
    | hb_map (hb_add (this))
92
0
    | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); })
93
0
    ;
94
0
  }
95
96
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
97
0
  {
98
0
    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
99
0
    unsigned int count = pairSet.len;
100
0
    for (unsigned int i = 0; i < count; i++)
101
0
      (this+pairSet[i]).collect_glyphs (c, valueFormat);
102
0
  }
103
104
0
  const Coverage &get_coverage () const { return this+coverage; }
105
106
  struct external_cache_t
107
  {
108
    hb_ot_layout_mapping_cache_t coverage;
109
  };
110
  void *external_cache_create () const
111
0
  {
112
0
    external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t));
113
0
    if (likely (cache))
114
0
    {
115
0
      cache->coverage.clear ();
116
0
    }
117
0
    return cache;
118
0
  }
119
120
  bool apply (hb_ot_apply_context_t *c, void *external_cache) const
121
0
  {
122
0
    TRACE_APPLY (this);
123
124
0
    hb_buffer_t *buffer = c->buffer;
125
126
0
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
127
0
    external_cache_t *cache = (external_cache_t *) external_cache;
128
0
    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
129
#else
130
    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
131
#endif
132
0
    if (index == NOT_COVERED) return_trace (false);
133
134
0
    auto &skippy_iter = c->iter_input;
135
0
    skippy_iter.reset_fast (buffer->idx);
136
0
    unsigned unsafe_to;
137
0
    if (unlikely (!skippy_iter.next (&unsafe_to)))
138
0
    {
139
0
      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
140
0
      return_trace (false);
141
0
    }
142
143
0
    return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
144
0
  }
145
146
  bool subset (hb_subset_context_t *c) const
147
0
  {
148
0
    TRACE_SUBSET (this);
149
0
150
0
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
151
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
152
0
153
0
    auto *out = c->serializer->start_embed (*this);
154
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
155
0
    out->format = format;
156
0
157
0
    hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat[0], valueFormat[1]);
158
0
159
0
    if (c->plan->normalized_coords)
160
0
    {
161
0
      /* all device flags will be dropped when full instancing, no need to strip
162
0
       * hints, also do not strip emtpy cause we don't compute the new default
163
0
       * value during stripping */
164
0
      newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map);
165
0
    }
166
0
    /* do not strip hints for VF */
167
0
    else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
168
0
    {
169
0
      hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
170
0
      bool has_fvar = (blob != hb_blob_get_empty ());
171
0
      hb_blob_destroy (blob);
172
0
173
0
      bool strip = !has_fvar;
174
0
      /* special case: strip hints when a VF has no GDEF varstore after
175
0
       * subsetting*/
176
0
      if (has_fvar && !c->plan->has_gdef_varstore)
177
0
        strip = true;
178
0
      newFormats = compute_effective_value_formats (glyphset, strip, true);
179
0
    }
180
0
181
0
    out->valueFormat[0] = newFormats.first;
182
0
    out->valueFormat[1] = newFormats.second;
183
0
184
0
    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
185
0
186
0
    + hb_zip (this+coverage, pairSet)
187
0
    | hb_filter (glyphset, hb_first)
188
0
    | hb_filter ([this, c, out] (const typename Types::template OffsetTo<PairSet>& _)
189
0
                 {
190
0
                   auto snap = c->serializer->snapshot ();
191
0
                   auto *o = out->pairSet.serialize_append (c->serializer);
192
0
                   if (unlikely (!o)) return false;
193
0
                   bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat);
194
0
                   if (!ret)
195
0
                   {
196
0
                     out->pairSet.pop ();
197
0
                     c->serializer->revert (snap);
198
0
                   }
199
0
                   return ret;
200
0
                 },
201
0
                 hb_second)
202
0
    | hb_map (hb_first)
203
0
    | hb_map (glyph_map)
204
0
    | hb_sink (new_coverage)
205
0
    ;
206
0
207
0
    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
208
0
209
0
    return_trace (bool (new_coverage));
210
0
  }
211
212
213
  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset,
214
                                                                 bool strip_hints, bool strip_empty,
215
                                                                 const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
216
0
  {
217
0
    unsigned record_size = PairSet::get_size (valueFormat);
218
0
219
0
    unsigned format1 = 0;
220
0
    unsigned format2 = 0;
221
0
    for (const auto & _ :
222
0
    + hb_zip (this+coverage, pairSet)
223
0
    | hb_filter (glyphset, hb_first)
224
0
    | hb_map (hb_second)
225
0
  )
226
0
    {
227
0
      const PairSet& set = (this + _);
228
0
      const PairValueRecord *record = &set.firstPairValueRecord;
229
0
230
0
      unsigned count = set.len;
231
0
      for (unsigned i = 0; i < count; i++)
232
0
      {
233
0
        if (record->intersects (glyphset))
234
0
        {
235
0
          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 (), strip_hints, strip_empty, &set, varidx_delta_map);
236
0
          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map);
237
0
        }
238
0
        record = &StructAtOffset<const PairValueRecord> (record, record_size);
239
0
      }
240
0
241
0
      if (format1 == valueFormat[0] && format2 == valueFormat[1])
242
0
        break;
243
0
    }
244
0
245
0
    return hb_pair (format1, format2);
246
0
  }
247
};
248
249
250
}
251
}
252
}
253
254
#endif  // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH