xref: /aosp_15_r20/external/harfbuzz_ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 #ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
2 #define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
3 
4 #include "MarkMarkPosFormat1.hh"
5 
6 namespace OT {
7 namespace Layout {
8 namespace GPOS_impl {
9 
10 typedef AnchorMatrix Mark2Array;        /* mark2-major--
11                                          * in order of Mark2Coverage Index--,
12                                          * mark1-minor--
13                                          * ordered by class--zero-based. */
14 
15 template <typename Types>
16 struct MarkMarkPosFormat1_2
17 {
18   protected:
19   HBUINT16      format;                 /* Format identifier--format = 1 */
20   typename Types::template OffsetTo<Coverage>
21                 mark1Coverage;          /* Offset to Combining Mark1 Coverage
22                                          * table--from beginning of MarkMarkPos
23                                          * subtable */
24   typename Types::template OffsetTo<Coverage>
25                 mark2Coverage;          /* Offset to Combining Mark2 Coverage
26                                          * table--from beginning of MarkMarkPos
27                                          * subtable */
28   HBUINT16      classCount;             /* Number of defined mark classes */
29   typename Types::template OffsetTo<MarkArray>
30                 mark1Array;             /* Offset to Mark1Array table--from
31                                          * beginning of MarkMarkPos subtable */
32   typename Types::template OffsetTo<Mark2Array>
33                 mark2Array;             /* Offset to Mark2Array table--from
34                                          * beginning of MarkMarkPos subtable */
35   public:
36   DEFINE_SIZE_STATIC (4 + 4 * Types::size);
37 
sanitizeOT::Layout::GPOS_impl::MarkMarkPosFormat1_238   bool sanitize (hb_sanitize_context_t *c) const
39   {
40     TRACE_SANITIZE (this);
41     return_trace (c->check_struct (this) &&
42                   mark1Coverage.sanitize (c, this) &&
43                   mark2Coverage.sanitize (c, this) &&
44                   mark1Array.sanitize (c, this) &&
45 		  hb_barrier () &&
46                   mark2Array.sanitize (c, this, (unsigned int) classCount));
47   }
48 
intersectsOT::Layout::GPOS_impl::MarkMarkPosFormat1_249   bool intersects (const hb_set_t *glyphs) const
50   {
51     return (this+mark1Coverage).intersects (glyphs) &&
52            (this+mark2Coverage).intersects (glyphs);
53   }
54 
closure_lookupsOT::Layout::GPOS_impl::MarkMarkPosFormat1_255   void closure_lookups (hb_closure_lookups_context_t *c) const {}
56 
collect_variation_indicesOT::Layout::GPOS_impl::MarkMarkPosFormat1_257   void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
58   {
59     + hb_zip (this+mark1Coverage, this+mark1Array)
60     | hb_filter (c->glyph_set, hb_first)
61     | hb_map (hb_second)
62     | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); })
63     ;
64 
65     hb_map_t klass_mapping;
66     Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping);
67 
68     unsigned mark2_count = (this+mark2Array).rows;
69     auto mark2_iter =
70     + hb_zip (this+mark2Coverage, hb_range (mark2_count))
71     | hb_filter (c->glyph_set, hb_first)
72     | hb_map (hb_second)
73     ;
74 
75     hb_sorted_vector_t<unsigned> mark2_indexes;
76     for (const unsigned row : mark2_iter)
77     {
78       + hb_range ((unsigned) classCount)
79       | hb_filter (klass_mapping)
80       | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
81       | hb_sink (mark2_indexes)
82       ;
83     }
84     (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ());
85   }
86 
collect_glyphsOT::Layout::GPOS_impl::MarkMarkPosFormat1_287   void collect_glyphs (hb_collect_glyphs_context_t *c) const
88   {
89     if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return;
90     if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return;
91   }
92 
get_coverageOT::Layout::GPOS_impl::MarkMarkPosFormat1_293   const Coverage &get_coverage () const { return this+mark1Coverage; }
94 
applyOT::Layout::GPOS_impl::MarkMarkPosFormat1_295   bool apply (hb_ot_apply_context_t *c) const
96   {
97     TRACE_APPLY (this);
98     hb_buffer_t *buffer = c->buffer;
99     unsigned int mark1_index = (this+mark1Coverage).get_coverage  (buffer->cur().codepoint);
100     if (likely (mark1_index == NOT_COVERED)) return_trace (false);
101 
102     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
103     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
104     skippy_iter.reset_fast (buffer->idx);
105     skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags);
106     unsigned unsafe_from;
107     if (unlikely (!skippy_iter.prev (&unsafe_from)))
108     {
109       buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
110       return_trace (false);
111     }
112 
113     if (likely (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])))
114     {
115       buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
116       return_trace (false);
117     }
118 
119     unsigned int j = skippy_iter.idx;
120 
121     unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
122     unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
123     unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
124     unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
125 
126     if (likely (id1 == id2))
127     {
128       if (id1 == 0) /* Marks belonging to the same base. */
129         goto good;
130       else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
131         goto good;
132     }
133     else
134     {
135       /* If ligature ids don't match, it may be the case that one of the marks
136        * itself is a ligature.  In which case match. */
137       if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
138         goto good;
139     }
140 
141     /* Didn't match. */
142     buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
143     return_trace (false);
144 
145     good:
146     unsigned int mark2_index = (this+mark2Coverage).get_coverage  (buffer->info[j].codepoint);
147     if (mark2_index == NOT_COVERED)
148     {
149       buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
150       return_trace (false);
151     }
152 
153     return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
154   }
155 
subsetOT::Layout::GPOS_impl::MarkMarkPosFormat1_2156   bool subset (hb_subset_context_t *c) const
157   {
158     TRACE_SUBSET (this);
159     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
160     const hb_map_t &glyph_map = *c->plan->glyph_map;
161 
162     auto *out = c->serializer->start_embed (*this);
163     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
164     out->format = format;
165 
166     hb_map_t klass_mapping;
167     Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping);
168 
169     if (!klass_mapping.get_population ()) return_trace (false);
170     out->classCount = klass_mapping.get_population ();
171 
172     auto mark1_iter =
173     + hb_zip (this+mark1Coverage, this+mark1Array)
174     | hb_filter (glyphset, hb_first)
175     ;
176 
177     hb_sorted_vector_t<hb_codepoint_t> new_coverage;
178     + mark1_iter
179     | hb_map (hb_first)
180     | hb_map (glyph_map)
181     | hb_sink (new_coverage)
182     ;
183 
184     if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
185       return_trace (false);
186 
187     if (unlikely (!out->mark1Array.serialize_subset (c, mark1Array, this,
188 						     (this+mark1Coverage).iter (),
189 						     &klass_mapping)))
190       return_trace (false);
191 
192     unsigned mark2count = (this+mark2Array).rows;
193     auto mark2_iter =
194     + hb_zip (this+mark2Coverage, hb_range (mark2count))
195     | hb_filter (glyphset, hb_first)
196     ;
197 
198     new_coverage.reset ();
199     + mark2_iter
200     | hb_map (hb_first)
201     | hb_map (glyph_map)
202     | hb_sink (new_coverage)
203     ;
204 
205     if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
206       return_trace (false);
207 
208     hb_sorted_vector_t<unsigned> mark2_indexes;
209     for (const unsigned row : + mark2_iter
210                               | hb_map (hb_second))
211     {
212       + hb_range ((unsigned) classCount)
213       | hb_filter (klass_mapping)
214       | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
215       | hb_sink (mark2_indexes)
216       ;
217     }
218 
219     return_trace (out->mark2Array.serialize_subset (c, mark2Array, this,
220 						    mark2_iter.len (),
221 						    mark2_indexes.iter ()));
222 
223   }
224 };
225 
226 
227 }
228 }
229 }
230 
231 #endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */
232