xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-aat-map.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker  * Copyright © 2009,2010  Red Hat, Inc.
3*2d1272b8SAndroid Build Coastguard Worker  * Copyright © 2010,2011,2013  Google, Inc.
4*2d1272b8SAndroid Build Coastguard Worker  *
5*2d1272b8SAndroid Build Coastguard Worker  *  This is part of HarfBuzz, a text shaping library.
6*2d1272b8SAndroid Build Coastguard Worker  *
7*2d1272b8SAndroid Build Coastguard Worker  * Permission is hereby granted, without written agreement and without
8*2d1272b8SAndroid Build Coastguard Worker  * license or royalty fees, to use, copy, modify, and distribute this
9*2d1272b8SAndroid Build Coastguard Worker  * software and its documentation for any purpose, provided that the
10*2d1272b8SAndroid Build Coastguard Worker  * above copyright notice and the following two paragraphs appear in
11*2d1272b8SAndroid Build Coastguard Worker  * all copies of this software.
12*2d1272b8SAndroid Build Coastguard Worker  *
13*2d1272b8SAndroid Build Coastguard Worker  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14*2d1272b8SAndroid Build Coastguard Worker  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15*2d1272b8SAndroid Build Coastguard Worker  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16*2d1272b8SAndroid Build Coastguard Worker  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17*2d1272b8SAndroid Build Coastguard Worker  * DAMAGE.
18*2d1272b8SAndroid Build Coastguard Worker  *
19*2d1272b8SAndroid Build Coastguard Worker  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20*2d1272b8SAndroid Build Coastguard Worker  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21*2d1272b8SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22*2d1272b8SAndroid Build Coastguard Worker  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23*2d1272b8SAndroid Build Coastguard Worker  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24*2d1272b8SAndroid Build Coastguard Worker  *
25*2d1272b8SAndroid Build Coastguard Worker  * Red Hat Author(s): Behdad Esfahbod
26*2d1272b8SAndroid Build Coastguard Worker  * Google Author(s): Behdad Esfahbod
27*2d1272b8SAndroid Build Coastguard Worker  */
28*2d1272b8SAndroid Build Coastguard Worker 
29*2d1272b8SAndroid Build Coastguard Worker #include "hb.hh"
30*2d1272b8SAndroid Build Coastguard Worker 
31*2d1272b8SAndroid Build Coastguard Worker #ifndef HB_NO_AAT_SHAPE
32*2d1272b8SAndroid Build Coastguard Worker 
33*2d1272b8SAndroid Build Coastguard Worker #include "hb-aat-map.hh"
34*2d1272b8SAndroid Build Coastguard Worker 
35*2d1272b8SAndroid Build Coastguard Worker #include "hb-aat-layout.hh"
36*2d1272b8SAndroid Build Coastguard Worker #include "hb-aat-layout-feat-table.hh"
37*2d1272b8SAndroid Build Coastguard Worker 
38*2d1272b8SAndroid Build Coastguard Worker 
add_feature(const hb_feature_t & feature)39*2d1272b8SAndroid Build Coastguard Worker void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature)
40*2d1272b8SAndroid Build Coastguard Worker {
41*2d1272b8SAndroid Build Coastguard Worker   if (!face->table.feat->has_data ()) return;
42*2d1272b8SAndroid Build Coastguard Worker 
43*2d1272b8SAndroid Build Coastguard Worker   if (feature.tag == HB_TAG ('a','a','l','t'))
44*2d1272b8SAndroid Build Coastguard Worker   {
45*2d1272b8SAndroid Build Coastguard Worker     if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
46*2d1272b8SAndroid Build Coastguard Worker       return;
47*2d1272b8SAndroid Build Coastguard Worker     feature_range_t *range = features.push();
48*2d1272b8SAndroid Build Coastguard Worker     range->start = feature.start;
49*2d1272b8SAndroid Build Coastguard Worker     range->end = feature.end;
50*2d1272b8SAndroid Build Coastguard Worker     range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
51*2d1272b8SAndroid Build Coastguard Worker     range->info.setting = (hb_aat_layout_feature_selector_t) feature.value;
52*2d1272b8SAndroid Build Coastguard Worker     range->info.seq = features.length;
53*2d1272b8SAndroid Build Coastguard Worker     range->info.is_exclusive = true;
54*2d1272b8SAndroid Build Coastguard Worker     return;
55*2d1272b8SAndroid Build Coastguard Worker   }
56*2d1272b8SAndroid Build Coastguard Worker 
57*2d1272b8SAndroid Build Coastguard Worker   const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag);
58*2d1272b8SAndroid Build Coastguard Worker   if (!mapping) return;
59*2d1272b8SAndroid Build Coastguard Worker 
60*2d1272b8SAndroid Build Coastguard Worker   const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType);
61*2d1272b8SAndroid Build Coastguard Worker   if (!feature_name->has_data ())
62*2d1272b8SAndroid Build Coastguard Worker   {
63*2d1272b8SAndroid Build Coastguard Worker     /* Special case: Chain::compile_flags will fall back to the deprecated version of
64*2d1272b8SAndroid Build Coastguard Worker      * small-caps if necessary, so we need to check for that possibility.
65*2d1272b8SAndroid Build Coastguard Worker      * https://github.com/harfbuzz/harfbuzz/issues/2307 */
66*2d1272b8SAndroid Build Coastguard Worker     if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
67*2d1272b8SAndroid Build Coastguard Worker 	mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
68*2d1272b8SAndroid Build Coastguard Worker     {
69*2d1272b8SAndroid Build Coastguard Worker       feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
70*2d1272b8SAndroid Build Coastguard Worker       if (!feature_name->has_data ()) return;
71*2d1272b8SAndroid Build Coastguard Worker     }
72*2d1272b8SAndroid Build Coastguard Worker     else return;
73*2d1272b8SAndroid Build Coastguard Worker   }
74*2d1272b8SAndroid Build Coastguard Worker 
75*2d1272b8SAndroid Build Coastguard Worker   feature_range_t *range = features.push();
76*2d1272b8SAndroid Build Coastguard Worker   range->start = feature.start;
77*2d1272b8SAndroid Build Coastguard Worker   range->end = feature.end;
78*2d1272b8SAndroid Build Coastguard Worker   range->info.type = mapping->aatFeatureType;
79*2d1272b8SAndroid Build Coastguard Worker   range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable;
80*2d1272b8SAndroid Build Coastguard Worker   range->info.seq = features.length;
81*2d1272b8SAndroid Build Coastguard Worker   range->info.is_exclusive = feature_name->is_exclusive ();
82*2d1272b8SAndroid Build Coastguard Worker }
83*2d1272b8SAndroid Build Coastguard Worker 
84*2d1272b8SAndroid Build Coastguard Worker void
compile(hb_aat_map_t & m)85*2d1272b8SAndroid Build Coastguard Worker hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
86*2d1272b8SAndroid Build Coastguard Worker {
87*2d1272b8SAndroid Build Coastguard Worker   /* Compute active features per range, and compile each. */
88*2d1272b8SAndroid Build Coastguard Worker 
89*2d1272b8SAndroid Build Coastguard Worker   /* Sort features by start/end events. */
90*2d1272b8SAndroid Build Coastguard Worker   hb_vector_t<feature_event_t> feature_events;
91*2d1272b8SAndroid Build Coastguard Worker   for (unsigned int i = 0; i < features.length; i++)
92*2d1272b8SAndroid Build Coastguard Worker   {
93*2d1272b8SAndroid Build Coastguard Worker     auto &feature = features[i];
94*2d1272b8SAndroid Build Coastguard Worker 
95*2d1272b8SAndroid Build Coastguard Worker     if (features[i].start == features[i].end)
96*2d1272b8SAndroid Build Coastguard Worker       continue;
97*2d1272b8SAndroid Build Coastguard Worker 
98*2d1272b8SAndroid Build Coastguard Worker     feature_event_t *event;
99*2d1272b8SAndroid Build Coastguard Worker 
100*2d1272b8SAndroid Build Coastguard Worker     event = feature_events.push ();
101*2d1272b8SAndroid Build Coastguard Worker     event->index = features[i].start;
102*2d1272b8SAndroid Build Coastguard Worker     event->start = true;
103*2d1272b8SAndroid Build Coastguard Worker     event->feature = feature.info;
104*2d1272b8SAndroid Build Coastguard Worker 
105*2d1272b8SAndroid Build Coastguard Worker     event = feature_events.push ();
106*2d1272b8SAndroid Build Coastguard Worker     event->index = features[i].end;
107*2d1272b8SAndroid Build Coastguard Worker     event->start = false;
108*2d1272b8SAndroid Build Coastguard Worker     event->feature = feature.info;
109*2d1272b8SAndroid Build Coastguard Worker   }
110*2d1272b8SAndroid Build Coastguard Worker   feature_events.qsort ();
111*2d1272b8SAndroid Build Coastguard Worker   /* Add a strategic final event. */
112*2d1272b8SAndroid Build Coastguard Worker   {
113*2d1272b8SAndroid Build Coastguard Worker     feature_info_t feature;
114*2d1272b8SAndroid Build Coastguard Worker     feature.seq = features.length + 1;
115*2d1272b8SAndroid Build Coastguard Worker 
116*2d1272b8SAndroid Build Coastguard Worker     feature_event_t *event = feature_events.push ();
117*2d1272b8SAndroid Build Coastguard Worker     event->index = -1; /* This value does magic. */
118*2d1272b8SAndroid Build Coastguard Worker     event->start = false;
119*2d1272b8SAndroid Build Coastguard Worker     event->feature = feature;
120*2d1272b8SAndroid Build Coastguard Worker   }
121*2d1272b8SAndroid Build Coastguard Worker 
122*2d1272b8SAndroid Build Coastguard Worker   /* Scan events and save features for each range. */
123*2d1272b8SAndroid Build Coastguard Worker   hb_sorted_vector_t<feature_info_t> active_features;
124*2d1272b8SAndroid Build Coastguard Worker   unsigned int last_index = 0;
125*2d1272b8SAndroid Build Coastguard Worker   for (unsigned int i = 0; i < feature_events.length; i++)
126*2d1272b8SAndroid Build Coastguard Worker   {
127*2d1272b8SAndroid Build Coastguard Worker     feature_event_t *event = &feature_events[i];
128*2d1272b8SAndroid Build Coastguard Worker 
129*2d1272b8SAndroid Build Coastguard Worker     if (event->index != last_index)
130*2d1272b8SAndroid Build Coastguard Worker     {
131*2d1272b8SAndroid Build Coastguard Worker       /* Save a snapshot of active features and the range. */
132*2d1272b8SAndroid Build Coastguard Worker 
133*2d1272b8SAndroid Build Coastguard Worker       /* Sort features and merge duplicates */
134*2d1272b8SAndroid Build Coastguard Worker       current_features = active_features;
135*2d1272b8SAndroid Build Coastguard Worker       range_first = last_index;
136*2d1272b8SAndroid Build Coastguard Worker       range_last = event->index - 1;
137*2d1272b8SAndroid Build Coastguard Worker       if (current_features.length)
138*2d1272b8SAndroid Build Coastguard Worker       {
139*2d1272b8SAndroid Build Coastguard Worker 	current_features.qsort ();
140*2d1272b8SAndroid Build Coastguard Worker 	unsigned int j = 0;
141*2d1272b8SAndroid Build Coastguard Worker 	for (unsigned int i = 1; i < current_features.length; i++)
142*2d1272b8SAndroid Build Coastguard Worker 	  if (current_features[i].type != current_features[j].type ||
143*2d1272b8SAndroid Build Coastguard Worker 	      /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
144*2d1272b8SAndroid Build Coastguard Worker 	       * respectively, so we mask out the low-order bit when checking for "duplicates"
145*2d1272b8SAndroid Build Coastguard Worker 	       * (selectors referring to the same feature setting) here. */
146*2d1272b8SAndroid Build Coastguard Worker 	      (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
147*2d1272b8SAndroid Build Coastguard Worker 	    current_features[++j] = current_features[i];
148*2d1272b8SAndroid Build Coastguard Worker 	current_features.shrink (j + 1);
149*2d1272b8SAndroid Build Coastguard Worker       }
150*2d1272b8SAndroid Build Coastguard Worker 
151*2d1272b8SAndroid Build Coastguard Worker       hb_aat_layout_compile_map (this, &m);
152*2d1272b8SAndroid Build Coastguard Worker 
153*2d1272b8SAndroid Build Coastguard Worker       last_index = event->index;
154*2d1272b8SAndroid Build Coastguard Worker     }
155*2d1272b8SAndroid Build Coastguard Worker 
156*2d1272b8SAndroid Build Coastguard Worker     if (event->start)
157*2d1272b8SAndroid Build Coastguard Worker     {
158*2d1272b8SAndroid Build Coastguard Worker       active_features.push (event->feature);
159*2d1272b8SAndroid Build Coastguard Worker     } else {
160*2d1272b8SAndroid Build Coastguard Worker       feature_info_t *feature = active_features.lsearch (event->feature);
161*2d1272b8SAndroid Build Coastguard Worker       if (feature)
162*2d1272b8SAndroid Build Coastguard Worker 	active_features.remove_ordered (feature - active_features.arrayZ);
163*2d1272b8SAndroid Build Coastguard Worker     }
164*2d1272b8SAndroid Build Coastguard Worker   }
165*2d1272b8SAndroid Build Coastguard Worker 
166*2d1272b8SAndroid Build Coastguard Worker   for (auto &chain_flags : m.chain_flags)
167*2d1272b8SAndroid Build Coastguard Worker     // With our above setup this value is one less than desired; adjust it.
168*2d1272b8SAndroid Build Coastguard Worker     chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END;
169*2d1272b8SAndroid Build Coastguard Worker }
170*2d1272b8SAndroid Build Coastguard Worker 
171*2d1272b8SAndroid Build Coastguard Worker 
172*2d1272b8SAndroid Build Coastguard Worker #endif
173