xref: /aosp_15_r20/external/harfbuzz_ng/src/OT/Layout/GPOS/ValueFormat.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
2 #define OT_LAYOUT_GPOS_VALUEFORMAT_HH
3 
4 #include "../../../hb-ot-layout-gsubgpos.hh"
5 
6 namespace OT {
7 namespace Layout {
8 namespace GPOS_impl {
9 
10 typedef HBUINT16 Value;
11 
12 struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases.
13 
14 typedef UnsizedArrayOf<Value> ValueRecord;
15 
16 struct ValueFormat : HBUINT16
17 {
18   enum Flags {
19     xPlacement  = 0x0001u,      /* Includes horizontal adjustment for placement */
20     yPlacement  = 0x0002u,      /* Includes vertical adjustment for placement */
21     xAdvance    = 0x0004u,      /* Includes horizontal adjustment for advance */
22     yAdvance    = 0x0008u,      /* Includes vertical adjustment for advance */
23     xPlaDevice  = 0x0010u,      /* Includes horizontal Device table for placement */
24     yPlaDevice  = 0x0020u,      /* Includes vertical Device table for placement */
25     xAdvDevice  = 0x0040u,      /* Includes horizontal Device table for advance */
26     yAdvDevice  = 0x0080u,      /* Includes vertical Device table for advance */
27     ignored     = 0x0F00u,      /* Was used in TrueType Open for MM fonts */
28     reserved    = 0xF000u,      /* For future use */
29 
30     devices     = 0x00F0u       /* Mask for having any Device table */
31   };
32 
33 /* All fields are options.  Only those available advance the value pointer. */
34 #if 0
35   HBINT16               xPlacement;     /* Horizontal adjustment for
36                                          * placement--in design units */
37   HBINT16               yPlacement;     /* Vertical adjustment for
38                                          * placement--in design units */
39   HBINT16               xAdvance;       /* Horizontal adjustment for
40                                          * advance--in design units (only used
41                                          * for horizontal writing) */
42   HBINT16               yAdvance;       /* Vertical adjustment for advance--in
43                                          * design units (only used for vertical
44                                          * writing) */
45   Offset16To<Device>    xPlaDevice;     /* Offset to Device table for
46                                          * horizontal placement--measured from
47                                          * beginning of PosTable (may be NULL) */
48   Offset16To<Device>    yPlaDevice;     /* Offset to Device table for vertical
49                                          * placement--measured from beginning
50                                          * of PosTable (may be NULL) */
51   Offset16To<Device>    xAdvDevice;     /* Offset to Device table for
52                                          * horizontal advance--measured from
53                                          * beginning of PosTable (may be NULL) */
54   Offset16To<Device>    yAdvDevice;     /* Offset to Device table for vertical
55                                          * advance--measured from beginning of
56                                          * PosTable (may be NULL) */
57 #endif
58 
operator =OT::Layout::GPOS_impl::ValueFormat59   IntType& operator = (uint16_t i) { v = i; return *this; }
60 
get_lenOT::Layout::GPOS_impl::ValueFormat61   unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
get_sizeOT::Layout::GPOS_impl::ValueFormat62   unsigned int get_size () const { return get_len () * Value::static_size; }
63 
get_device_table_indicesOT::Layout::GPOS_impl::ValueFormat64   hb_vector_t<unsigned> get_device_table_indices () const {
65     unsigned i = 0;
66     hb_vector_t<unsigned> result;
67     unsigned format = *this;
68 
69     if (format & xPlacement) i++;
70     if (format & yPlacement) i++;
71     if (format & xAdvance)   i++;
72     if (format & yAdvance)   i++;
73 
74     if (format & xPlaDevice) result.push (i++);
75     if (format & yPlaDevice) result.push (i++);
76     if (format & xAdvDevice) result.push (i++);
77     if (format & yAdvDevice) result.push (i++);
78 
79     return result;
80   }
81 
apply_valueOT::Layout::GPOS_impl::ValueFormat82   bool apply_value (hb_ot_apply_context_t *c,
83                     const ValueBase       *base,
84                     const Value           *values,
85                     hb_glyph_position_t   &glyph_pos) const
86   {
87     bool ret = false;
88     unsigned int format = *this;
89     if (!format) return ret;
90 
91     hb_font_t *font = c->font;
92     bool horizontal =
93 #ifndef HB_NO_VERTICAL
94       HB_DIRECTION_IS_HORIZONTAL (c->direction)
95 #else
96       true
97 #endif
98       ;
99 
100     if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
101     if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
102     if (format & xAdvance) {
103       if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
104       values++;
105     }
106     /* y_advance values grow downward but font-space grows upward, hence negation */
107     if (format & yAdvance) {
108       if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
109       values++;
110     }
111 
112     if (!has_device ()) return ret;
113 
114     bool use_x_device = font->x_ppem || font->num_coords;
115     bool use_y_device = font->y_ppem || font->num_coords;
116 
117     if (!use_x_device && !use_y_device) return ret;
118 
119     const ItemVariationStore &store = c->var_store;
120     auto *cache = c->var_store_cache;
121 
122     /* pixel -> fractional pixel */
123     if (format & xPlaDevice)
124     {
125       if (use_x_device) glyph_pos.x_offset  += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
126       values++;
127     }
128     if (format & yPlaDevice)
129     {
130       if (use_y_device) glyph_pos.y_offset  += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
131       values++;
132     }
133     if (format & xAdvDevice)
134     {
135       if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
136       values++;
137     }
138     if (format & yAdvDevice)
139     {
140       /* y_advance values grow downward but font-space grows upward, hence negation */
141       if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
142       values++;
143     }
144     return ret;
145   }
146 
get_effective_formatOT::Layout::GPOS_impl::ValueFormat147   unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
148                                      const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
149   {
150     unsigned int format = *this;
151     for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
152       if (format & flag)
153       {
154         if (strip_hints && flag >= xPlaDevice)
155         {
156           format = format & ~flag;
157           values++;
158           continue;
159         }
160         if (varidx_delta_map && flag >= xPlaDevice)
161         {
162           update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
163           continue;
164         }
165         /* do not strip empty when instancing, cause we don't know whether the new
166          * default value is 0 or not */
167         if (strip_empty) should_drop (*values, (Flags) flag, &format);
168         values++;
169       }
170     }
171 
172     return format;
173   }
174 
175   template<typename Iterator,
176       hb_requires (hb_is_iterator (Iterator))>
get_effective_formatOT::Layout::GPOS_impl::ValueFormat177   unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
178                                      const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const {
179     unsigned int new_format = 0;
180 
181     for (const hb_array_t<const Value>& values : it)
182       new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
183 
184     return new_format;
185   }
186 
copy_valuesOT::Layout::GPOS_impl::ValueFormat187   void copy_values (hb_serialize_context_t *c,
188                     unsigned int new_format,
189                     const ValueBase *base,
190                     const Value *values,
191                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
192   {
193     unsigned int format = *this;
194     if (!format) return;
195 
196     HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
197     if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++);
198     if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++);
199     if (format & xAdvance)   x_adv = copy_value (c, new_format, xAdvance, *values++);
200     if (format & yAdvance)   y_adv = copy_value (c, new_format, yAdvance, *values++);
201 
202     if (!has_device ())
203       return;
204 
205     if (format & xPlaDevice)
206     {
207       add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
208       copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
209     }
210 
211     if (format & yPlaDevice)
212     {
213       add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
214       copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
215     }
216 
217     if (format & xAdvDevice)
218     {
219       add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
220       copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
221     }
222 
223     if (format & yAdvDevice)
224     {
225       add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
226       copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
227     }
228   }
229 
copy_valueOT::Layout::GPOS_impl::ValueFormat230   HBINT16* copy_value (hb_serialize_context_t *c,
231                        unsigned int new_format,
232                        Flags flag,
233                        Value value) const
234   {
235     // Filter by new format.
236     if (!(new_format & flag)) return nullptr;
237     return reinterpret_cast<HBINT16 *> (c->copy (value));
238   }
239 
collect_variation_indicesOT::Layout::GPOS_impl::ValueFormat240   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
241                                   const ValueBase *base,
242                                   const hb_array_t<const Value>& values) const
243   {
244     unsigned format = *this;
245     unsigned i = 0;
246     if (format & xPlacement) i++;
247     if (format & yPlacement) i++;
248     if (format & xAdvance) i++;
249     if (format & yAdvance) i++;
250     if (format & xPlaDevice)
251     {
252       (base + get_device (&(values[i]))).collect_variation_indices (c);
253       i++;
254     }
255 
256     if (format & ValueFormat::yPlaDevice)
257     {
258       (base + get_device (&(values[i]))).collect_variation_indices (c);
259       i++;
260     }
261 
262     if (format & ValueFormat::xAdvDevice)
263     {
264       (base + get_device (&(values[i]))).collect_variation_indices (c);
265       i++;
266     }
267 
268     if (format & ValueFormat::yAdvDevice)
269     {
270       (base + get_device (&(values[i]))).collect_variation_indices (c);
271       i++;
272     }
273   }
274 
275   private:
sanitize_value_devicesOT::Layout::GPOS_impl::ValueFormat276   bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
277   {
278     unsigned int format = *this;
279 
280     if (format & xPlacement) values++;
281     if (format & yPlacement) values++;
282     if (format & xAdvance)   values++;
283     if (format & yAdvance)   values++;
284 
285     if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
286     if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
287     if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
288     if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
289 
290     return true;
291   }
292 
get_deviceOT::Layout::GPOS_impl::ValueFormat293   static inline Offset16To<Device, ValueBase>& get_device (Value* value)
294   {
295     return *static_cast<Offset16To<Device, ValueBase> *> (value);
296   }
get_deviceOT::Layout::GPOS_impl::ValueFormat297   static inline const Offset16To<Device, ValueBase>& get_device (const Value* value)
298   {
299     return *static_cast<const Offset16To<Device, ValueBase> *> (value);
300   }
get_deviceOT::Layout::GPOS_impl::ValueFormat301   static inline const Device& get_device (const Value* value,
302 					  bool *worked,
303 					  const ValueBase *base,
304 					  hb_sanitize_context_t &c)
305   {
306     if (worked) *worked |= bool (*value);
307     auto &offset = *static_cast<const Offset16To<Device> *> (value);
308 
309     if (unlikely (!offset.sanitize (&c, base)))
310       return Null(Device);
311     hb_barrier ();
312 
313     return base + offset;
314   }
315 
add_delta_to_valueOT::Layout::GPOS_impl::ValueFormat316   void add_delta_to_value (HBINT16 *value,
317                            const ValueBase *base,
318                            const Value *src_value,
319                            const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
320   {
321     if (!value) return;
322     unsigned varidx = (base + get_device (src_value)).get_variation_index ();
323     hb_pair_t<unsigned, int> *varidx_delta;
324     if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
325 
326     *value += hb_second (*varidx_delta);
327   }
328 
copy_deviceOT::Layout::GPOS_impl::ValueFormat329   bool copy_device (hb_serialize_context_t *c,
330                     const ValueBase *base,
331                     const Value *src_value,
332                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
333                     unsigned int new_format, Flags flag) const
334   {
335     // Filter by new format.
336     if (!(new_format & flag)) return true;
337 
338     Value       *dst_value = c->copy (*src_value);
339 
340     if (!dst_value) return false;
341     if (*dst_value == 0) return true;
342 
343     *dst_value = 0;
344     c->push ();
345     if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
346     {
347       c->add_link (*dst_value, c->pop_pack ());
348       return true;
349     }
350     else
351     {
352       c->pop_discard ();
353       return false;
354     }
355   }
356 
get_shortOT::Layout::GPOS_impl::ValueFormat357   static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
358   {
359     if (worked) *worked |= bool (*value);
360     return *reinterpret_cast<const HBINT16 *> (value);
361   }
362 
363   public:
364 
has_deviceOT::Layout::GPOS_impl::ValueFormat365   bool has_device () const
366   {
367     unsigned int format = *this;
368     return (format & devices) != 0;
369   }
370 
sanitize_valueOT::Layout::GPOS_impl::ValueFormat371   bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
372   {
373     TRACE_SANITIZE (this);
374 
375     if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
376 
377     if (c->lazy_some_gpos)
378       return_trace (true);
379 
380     return_trace (!has_device () || sanitize_value_devices (c, base, values));
381   }
382 
sanitize_valuesOT::Layout::GPOS_impl::ValueFormat383   bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
384   {
385     TRACE_SANITIZE (this);
386     unsigned size = get_size ();
387 
388     if (!c->check_range (values, count, size)) return_trace (false);
389 
390     if (c->lazy_some_gpos)
391       return_trace (true);
392 
393     hb_barrier ();
394     return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
395   }
396 
397   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
sanitize_values_stride_unsafeOT::Layout::GPOS_impl::ValueFormat398   bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
399   {
400     TRACE_SANITIZE (this);
401 
402     if (!has_device ()) return_trace (true);
403 
404     for (unsigned int i = 0; i < count; i++) {
405       if (!sanitize_value_devices (c, base, values))
406         return_trace (false);
407       values = &StructAtOffset<const Value> (values, stride);
408     }
409 
410     return_trace (true);
411   }
412 
413  private:
414 
should_dropOT::Layout::GPOS_impl::ValueFormat415   void should_drop (Value value, Flags flag, unsigned int* format) const
416   {
417     if (value) return;
418     *format = *format & ~flag;
419   }
420 
update_var_flagOT::Layout::GPOS_impl::ValueFormat421   void update_var_flag (const Value* value, Flags flag,
422                         unsigned int* format, const ValueBase *base,
423                         const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
424   {
425     if (*value)
426     {
427       unsigned varidx = (base + get_device (value)).get_variation_index ();
428       hb_pair_t<unsigned, int> *varidx_delta;
429       if (varidx_delta_map->has (varidx, &varidx_delta) &&
430           varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
431         return;
432     }
433     *format = *format & ~flag;
434   }
435 };
436 
437 }
438 }
439 }
440 
441 #endif  // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
442