1use std::collections::HashMap;
4
5use geo_types::Geometry;
6use geozero::{
7 geo_types::GeoWriter, ColumnValue, FeatureProcessor, GeomProcessor, PropertyProcessor,
8};
9use lyon::{
10 geom::euclid::{Box2D, Point2D},
11 tessellation::VertexBuffers,
12};
13use widestring::U16String;
14
15use crate::{
16 euclid::{Rect, Size2D},
17 legacy::{
18 bidi::{apply_arabic_shaping, Char16},
19 buckets::symbol_bucket::SymbolBucketBuffer,
20 font_stack::FontStackHasher,
21 geometry_tile_data::{GeometryCoordinates, SymbolGeometryTileLayer},
22 glyph::{Glyph, GlyphDependencies, GlyphMap, GlyphMetrics, Glyphs},
23 glyph_atlas::{GlyphPosition, GlyphPositionMap, GlyphPositions},
24 image::ImageMap,
25 image_atlas::ImagePositions,
26 layout::{
27 layout::{BucketParameters, LayerTypeInfo, LayoutParameters},
28 symbol_feature::{SymbolGeometryTileFeature, VectorGeometryTileFeature},
29 symbol_layout::{FeatureIndex, LayerProperties, SymbolLayer, SymbolLayout},
30 },
31 style_types::SymbolLayoutProperties_Unevaluated,
32 tagged_string::TaggedString,
33 CanonicalTileID, MapMode, OverscaledTileID, TileSpace,
34 },
35 render::shaders::ShaderSymbolVertexNew,
36 sdf::{tessellation::IndexDataType, text::GlyphSet, Feature},
37};
38
39type GeoResult<T> = geozero::error::Result<T>;
40
41pub struct TextTessellatorNew {
43 geo_writer: GeoWriter,
44
45 text_field: String,
47
48 pub quad_buffer: VertexBuffers<ShaderSymbolVertexNew, IndexDataType>,
50 pub features: Vec<Feature>,
51
52 collected_features: Vec<(String, f64, f64)>,
54
55 current_index: usize,
57 current_text: Option<String>,
58 current_origin: Option<Box2D<f32, TileSpace>>,
59 current_point: Option<(f64, f64)>,
60}
61
62impl TextTessellatorNew {
63 pub fn finish(&mut self) {
64 let data = include_bytes!("../../../data/0-255.pbf");
65 let glyphs = GlyphSet::try_from(data.as_slice()).unwrap();
66
67 let font_stack = vec![
68 "Open Sans Regular".to_string(),
69 "Arial Unicode MS Regular".to_string(),
70 ];
71
72 let layer_name = "layer".to_string();
73
74 let mut glyph_dependencies = GlyphDependencies::new();
75
76 let tile_id = OverscaledTileID {
77 canonical: CanonicalTileID { x: 0, y: 0, z: 0 },
78 overscaled_z: 0,
79 };
80 let mut parameters = BucketParameters {
81 tile_id: tile_id,
82 mode: MapMode::Continuous,
83 pixel_ratio: 1.0,
84 layer_type: LayerTypeInfo,
85 };
86
87 let features: Vec<SymbolGeometryTileFeature> = self
90 .collected_features
91 .iter()
92 .map(|(text, x, y)| {
93 let geometry = vec![GeometryCoordinates(vec![Point2D::new(
94 *x as i16, *y as i16,
95 )])];
96 let mut feature =
97 SymbolGeometryTileFeature::new(Box::new(VectorGeometryTileFeature {
98 geometry,
99 }));
100 let mut tagged_string = TaggedString::default();
101 tagged_string.add_text_section(
102 &apply_arabic_shaping(&U16String::from(text.as_str())),
103 1.0,
104 font_stack.clone(),
105 None,
106 );
107 feature.formatted_text = Some(tagged_string);
108 feature
109 })
110 .collect();
111
112 if features.is_empty() {
113 return;
114 }
115
116 let layer_data = SymbolGeometryTileLayer {
117 name: layer_name.clone(),
118 features,
119 };
120 let layer_properties = vec![LayerProperties {
121 id: layer_name.clone(),
122 layer: SymbolLayer {
123 layout: SymbolLayoutProperties_Unevaluated,
124 },
125 }];
126
127 let image_positions = ImagePositions::new();
128
129 let glyph_map =
130 GlyphPositionMap::from_iter(glyphs.glyphs.iter().map(|(unicode_point, glyph)| {
131 (
132 *unicode_point as Char16,
133 GlyphPosition {
134 rect: Rect::new(
135 Point2D::new(
136 glyph.tex_origin_x as u16 + 3,
137 glyph.tex_origin_y as u16 + 3,
138 ),
139 Size2D::new(
140 glyph.buffered_dimensions().0 as u16,
141 glyph.buffered_dimensions().1 as u16,
142 ),
143 ), metrics: GlyphMetrics {
145 width: glyph.width,
146 height: glyph.height,
147 left: glyph.left_bearing,
148 top: glyph.top_bearing,
149 advance: glyph.h_advance,
150 },
151 },
152 )
153 }));
154
155 let glyph_positions: GlyphPositions =
156 GlyphPositions::from([(FontStackHasher::new(&font_stack), glyph_map)]);
157
158 let glyphs: GlyphMap = GlyphMap::from([(
159 FontStackHasher::new(&font_stack),
160 Glyphs::from_iter(glyphs.glyphs.iter().map(|(unicode_point, glyph)| {
161 (
162 *unicode_point as Char16,
163 Some(Glyph {
164 id: *unicode_point as Char16,
165 bitmap: Default::default(),
166 metrics: GlyphMetrics {
167 width: glyph.width,
168 height: glyph.height,
169 left: glyph.left_bearing,
170 top: glyph.top_bearing,
171 advance: glyph.h_advance,
172 },
173 }),
174 )
175 })),
176 )]);
177
178 let mut layout = SymbolLayout::new(
179 ¶meters,
180 &layer_properties,
181 Box::new(layer_data),
182 &mut LayoutParameters {
183 bucket_parameters: &mut parameters.clone(),
184 glyph_dependencies: &mut glyph_dependencies,
185 image_dependencies: &mut Default::default(),
186 available_images: &mut Default::default(),
187 },
188 )
189 .unwrap();
190
191 let empty_image_map = ImageMap::new();
192 layout.prepare_symbols(
193 &glyphs,
194 &glyph_positions,
195 &empty_image_map,
196 &image_positions,
197 );
198
199 let mut output = HashMap::new();
200 layout.create_bucket(
201 image_positions,
202 Box::new(FeatureIndex),
203 &mut output,
204 false,
205 false,
206 &tile_id.canonical,
207 );
208
209 let new_buffer = output.remove(&layer_name).unwrap();
210
211 let mut buffer = VertexBuffers::new();
212 let text_buffer = new_buffer.bucket.text;
213 let SymbolBucketBuffer {
214 shared_vertices,
215 triangles,
216 ..
217 } = text_buffer;
218 buffer.vertices = shared_vertices
219 .iter()
220 .map(|v| ShaderSymbolVertexNew::new(v))
221 .collect();
222 buffer.indices = triangles.indices.iter().map(|i| *i as u32).collect();
223
224 self.quad_buffer = buffer;
225 }
226}
227
228impl TextTessellatorNew {
229 pub fn new(text_field: String) -> Self {
230 Self {
231 text_field,
232 ..Default::default()
233 }
234 }
235}
236
237impl Default for TextTessellatorNew {
238 fn default() -> Self {
239 Self {
240 geo_writer: Default::default(),
241 text_field: "name".to_string(),
242 quad_buffer: VertexBuffers::new(),
243 features: vec![],
244 collected_features: vec![],
245 current_index: 0,
246 current_text: None,
247 current_origin: None,
248 current_point: None,
249 }
250 }
251}
252
253impl GeomProcessor for TextTessellatorNew {
254 fn xy(&mut self, x: f64, y: f64, idx: usize) -> GeoResult<()> {
255 self.current_point = Some((x, y));
256 self.geo_writer.xy(x, y, idx)
257 }
258 fn point_begin(&mut self, idx: usize) -> GeoResult<()> {
259 self.geo_writer.point_begin(idx)
260 }
261 fn point_end(&mut self, idx: usize) -> GeoResult<()> {
262 self.geo_writer.point_end(idx)
263 }
264 fn multipoint_begin(&mut self, size: usize, idx: usize) -> GeoResult<()> {
265 self.geo_writer.multipoint_begin(size, idx)
266 }
267 fn linestring_begin(&mut self, tagged: bool, size: usize, idx: usize) -> GeoResult<()> {
268 self.geo_writer.linestring_begin(tagged, size, idx)
269 }
270 fn linestring_end(&mut self, tagged: bool, idx: usize) -> GeoResult<()> {
271 self.geo_writer.linestring_end(tagged, idx)
272 }
273 fn multilinestring_begin(&mut self, size: usize, idx: usize) -> GeoResult<()> {
274 self.geo_writer.multilinestring_begin(size, idx)
275 }
276 fn multilinestring_end(&mut self, idx: usize) -> GeoResult<()> {
277 self.geo_writer.multilinestring_end(idx)
278 }
279 fn polygon_begin(&mut self, tagged: bool, size: usize, idx: usize) -> GeoResult<()> {
280 self.geo_writer.polygon_begin(tagged, size, idx)
281 }
282 fn polygon_end(&mut self, tagged: bool, idx: usize) -> GeoResult<()> {
283 self.geo_writer.polygon_end(tagged, idx)
284 }
285 fn multipolygon_begin(&mut self, size: usize, idx: usize) -> GeoResult<()> {
286 self.geo_writer.multipolygon_begin(size, idx)
287 }
288 fn multipolygon_end(&mut self, idx: usize) -> GeoResult<()> {
289 self.geo_writer.multipolygon_end(idx)
290 }
291}
292
293impl PropertyProcessor for TextTessellatorNew {
294 fn property(
295 &mut self,
296 _idx: usize,
297 name: &str,
298 value: &ColumnValue,
299 ) -> geozero::error::Result<bool> {
300 if name == self.text_field {
301 match value {
302 ColumnValue::String(str) => {
303 self.current_text = Some(str.to_string());
304 }
305 _ => {}
306 }
307 }
308 Ok(true)
309 }
310}
311
312impl FeatureProcessor for TextTessellatorNew {
313 fn feature_end(&mut self, _idx: u64) -> geozero::error::Result<()> {
314 let geometry = self.geo_writer.take_geometry();
315
316 if let (Some(text), Some((x, y))) = (self.current_text.take(), self.current_point.take()) {
318 self.collected_features.push((text, x, y));
319 } else {
320 self.current_text = None;
321 self.current_point = None;
322 }
323
324 match geometry {
325 Some(Geometry::Point(_point)) => {}
326 Some(Geometry::Polygon(_polygon)) => {}
327 Some(Geometry::LineString(_linestring)) => {}
328 Some(Geometry::Line(_))
329 | Some(Geometry::MultiPoint(_))
330 | Some(Geometry::MultiLineString(_))
331 | Some(Geometry::MultiPolygon(_))
332 | Some(Geometry::GeometryCollection(_))
333 | Some(Geometry::Rect(_))
334 | Some(Geometry::Triangle(_)) => {
335 log::debug!("Unsupported geometry in text tesselation")
336 }
337 None => {
338 log::debug!("No geometry in feature")
339 }
340 };
341
342 Ok(())
343 }
344}