1use api::{ColorF, FontInstanceFlags, GlyphInstance, RasterSpace, Shadow, GlyphIndex};
6use api::units::{LayoutToWorldTransform, LayoutVector2D, RasterPixelScale, DevicePixelScale};
7use api::units::*;
8use crate::scene_building::{CreateShadow, IsVisible};
9use crate::frame_builder::FrameBuildingState;
10use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
11use crate::gpu_cache::GpuCache;
12use crate::intern;
13use crate::internal_types::LayoutPrimitiveInfo;
14use crate::picture::SurfaceInfo;
15use crate::prim_store::{PrimitiveOpacity, PrimitiveScratchBuffer};
16use crate::prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData};
17use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
18use crate::resource_cache::ResourceCache;
19use crate::util::MatrixHelpers;
20use crate::prim_store::{InternablePrimitive, PrimitiveInstanceKind, LayoutPointAu};
21use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
22use crate::space::SpaceSnapper;
23
24use std::ops;
25
26use super::{storage, VectorKey};
27
28#[cfg_attr(feature = "capture", derive(Serialize))]
29#[cfg_attr(feature = "replay", derive(Deserialize))]
30#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
31pub struct GlyphInstanceAu {
32 pub index: GlyphIndex,
33 pub point: LayoutPointAu,
34}
35
36#[cfg_attr(feature = "capture", derive(Serialize))]
38#[cfg_attr(feature = "replay", derive(Deserialize))]
39#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
40pub struct TextRunKey {
41 pub common: PrimKeyCommonData,
42 pub font: FontInstance,
43 pub glyphs: Vec<GlyphInstanceAu>,
44 pub shadow: bool,
45 pub requested_raster_space: RasterSpace,
46 pub reference_frame_offset: VectorKey,
47}
48
49impl TextRunKey {
50 pub fn new(
51 info: &LayoutPrimitiveInfo,
52 text_run: TextRun,
53 ) -> Self {
54 let glyphs = text_run
55 .glyphs
56 .iter()
57 .map(|glyph| {
58 GlyphInstanceAu {
59 index: glyph.index,
60 point: glyph.point.to_au(),
61 }
62 })
63 .collect();
64
65 TextRunKey {
66 common: info.into(),
67 font: text_run.font,
68 glyphs,
69 shadow: text_run.shadow,
70 requested_raster_space: text_run.requested_raster_space,
71 reference_frame_offset: text_run.reference_frame_offset.into(),
72 }
73 }
74}
75
76impl intern::InternDebug for TextRunKey {}
77
78#[cfg_attr(feature = "capture", derive(Serialize))]
79#[cfg_attr(feature = "replay", derive(Deserialize))]
80#[derive(MallocSizeOf)]
81pub struct TextRunTemplate {
82 pub common: PrimTemplateCommonData,
83 pub font: FontInstance,
84 pub glyphs: Vec<GlyphInstance>,
85}
86
87impl ops::Deref for TextRunTemplate {
88 type Target = PrimTemplateCommonData;
89 fn deref(&self) -> &Self::Target {
90 &self.common
91 }
92}
93
94impl ops::DerefMut for TextRunTemplate {
95 fn deref_mut(&mut self) -> &mut Self::Target {
96 &mut self.common
97 }
98}
99
100impl From<TextRunKey> for TextRunTemplate {
101 fn from(item: TextRunKey) -> Self {
102 let common = PrimTemplateCommonData::with_key_common(item.common);
103 let glyphs = item
104 .glyphs
105 .iter()
106 .map(|glyph| {
107 GlyphInstance {
108 index: glyph.index,
109 point: LayoutPoint::from_au(glyph.point),
110 }
111 })
112 .collect();
113
114 TextRunTemplate {
115 common,
116 font: item.font,
117 glyphs,
118 }
119 }
120}
121
122impl TextRunTemplate {
123 pub fn update(
128 &mut self,
129 frame_state: &mut FrameBuildingState,
130 ) {
131 self.write_prim_gpu_blocks(frame_state);
132 self.opacity = PrimitiveOpacity::translucent();
133 }
134
135 fn write_prim_gpu_blocks(
136 &mut self,
137 frame_state: &mut FrameBuildingState,
138 ) {
139 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
141 request.push(ColorF::from(self.font.color).premultiplied());
142
143 let mut gpu_block = [0.0; 4];
144 for (i, src) in self.glyphs.iter().enumerate() {
145 if (i & 1) == 0 {
148 gpu_block[0] = src.point.x;
149 gpu_block[1] = src.point.y;
150 } else {
151 gpu_block[2] = src.point.x;
152 gpu_block[3] = src.point.y;
153 request.push(gpu_block);
154 }
155 }
156
157 if (self.glyphs.len() & 1) != 0 {
160 request.push(gpu_block);
161 }
162
163 assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
164 }
165 }
166}
167
168pub type TextRunDataHandle = intern::Handle<TextRun>;
169
170#[derive(Debug, MallocSizeOf)]
171#[cfg_attr(feature = "capture", derive(Serialize))]
172#[cfg_attr(feature = "replay", derive(Deserialize))]
173pub struct TextRun {
174 pub font: FontInstance,
175 pub glyphs: Vec<GlyphInstance>,
176 pub shadow: bool,
177 pub requested_raster_space: RasterSpace,
178 pub reference_frame_offset: LayoutVector2D,
179}
180
181impl intern::Internable for TextRun {
182 type Key = TextRunKey;
183 type StoreData = TextRunTemplate;
184 type InternData = ();
185 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_TEXT_RUNS;
186}
187
188impl InternablePrimitive for TextRun {
189 fn into_key(
190 self,
191 info: &LayoutPrimitiveInfo,
192 ) -> TextRunKey {
193 TextRunKey::new(
194 info,
195 self,
196 )
197 }
198
199 fn make_instance_kind(
200 key: TextRunKey,
201 data_handle: TextRunDataHandle,
202 prim_store: &mut PrimitiveStore,
203 ) -> PrimitiveInstanceKind {
204 let reference_frame_offset = key.reference_frame_offset.into();
205
206 let run_index = prim_store.text_runs.push(TextRunPrimitive {
207 used_font: key.font.clone(),
208 glyph_keys_range: storage::Range::empty(),
209 reference_frame_relative_offset: reference_frame_offset,
210 snapped_reference_frame_relative_offset: reference_frame_offset,
211 shadow: key.shadow,
212 raster_scale: 1.0,
213 requested_raster_space: key.requested_raster_space,
214 });
215
216 PrimitiveInstanceKind::TextRun{ data_handle, run_index }
217 }
218}
219
220impl CreateShadow for TextRun {
221 fn create_shadow(
222 &self,
223 shadow: &Shadow,
224 blur_is_noop: bool,
225 current_raster_space: RasterSpace,
226 ) -> Self {
227 let mut font = FontInstance {
228 color: shadow.color.into(),
229 ..self.font.clone()
230 };
231 if shadow.blur_radius > 0.0 {
232 font.disable_subpixel_aa();
233 }
234
235 let requested_raster_space = if blur_is_noop {
236 current_raster_space
237 } else {
238 RasterSpace::Local(1.0)
239 };
240
241 TextRun {
242 font,
243 glyphs: self.glyphs.clone(),
244 shadow: true,
245 requested_raster_space,
246 reference_frame_offset: self.reference_frame_offset,
247 }
248 }
249}
250
251impl IsVisible for TextRun {
252 fn is_visible(&self) -> bool {
253 self.font.color.a > 0
254 }
255}
256
257#[derive(Debug)]
258#[cfg_attr(feature = "capture", derive(Serialize))]
259pub struct TextRunPrimitive {
260 pub used_font: FontInstance,
261 pub glyph_keys_range: storage::Range<GlyphKey>,
262 pub reference_frame_relative_offset: LayoutVector2D,
263 pub snapped_reference_frame_relative_offset: LayoutVector2D,
264 pub shadow: bool,
265 pub raster_scale: f32,
266 pub requested_raster_space: RasterSpace,
267}
268
269impl TextRunPrimitive {
270 pub fn update_font_instance(
271 &mut self,
272 specified_font: &FontInstance,
273 surface: &SurfaceInfo,
274 spatial_node_index: SpatialNodeIndex,
275 transform: &LayoutToWorldTransform,
276 allow_subpixel: bool,
277 raster_space: RasterSpace,
278 spatial_tree: &SpatialTree,
279 ) -> bool {
280 let raster_scale = raster_space.local_scale().unwrap_or(1.0).max(0.001);
287
288 let dps = surface.device_pixel_scale.0;
289 let font_size = specified_font.size.to_f32_px();
290
291 let quantized_scale = (dps * raster_scale * 100.0).round() / 100.0;
296 let mut device_font_size = font_size * quantized_scale;
297
298 let (use_subpixel_aa, transform_glyphs, texture_padding, oversized) = if raster_space != RasterSpace::Screen ||
304 transform.has_perspective_component() || !transform.has_2d_inverse()
305 {
306 (false, false, true, device_font_size > FONT_SIZE_LIMIT)
307 } else if transform.exceeds_2d_scale((FONT_SIZE_LIMIT / device_font_size) as f64) {
308 (false, false, true, true)
309 } else {
310 (true, !transform.is_simple_2d_translation(), false, false)
311 };
312
313 let font_transform = if transform_glyphs {
314 self.raster_scale = 1.0;
317 FontTransform::from(transform)
318 } else {
319 if oversized {
320 let limited_raster_scale = FONT_SIZE_LIMIT / (font_size * dps);
324 device_font_size = FONT_SIZE_LIMIT;
325
326 self.raster_scale = limited_raster_scale;
329 } else {
330 self.raster_scale = raster_scale;
334 }
335
336 FontTransform::identity()
338 };
339
340 self.snapped_reference_frame_relative_offset = if transform_glyphs {
349 self.reference_frame_relative_offset
352 } else {
353 let raster_pixel_scale = RasterPixelScale::new(surface.device_pixel_scale.0);
357
358 let snap_to_device = SpaceSnapper::new_with_target(
361 surface.raster_spatial_node_index,
362 spatial_node_index,
363 raster_pixel_scale,
364 spatial_tree,
365 );
366 snap_to_device.snap_point(&self.reference_frame_relative_offset.to_point()).to_vector()
367 };
368
369 let mut flags = specified_font.flags;
370 if transform_glyphs {
371 flags |= FontInstanceFlags::TRANSFORM_GLYPHS;
372 }
373 if texture_padding {
374 flags |= FontInstanceFlags::TEXTURE_PADDING;
375 }
376
377 let cache_dirty =
380 self.used_font.transform != font_transform ||
381 self.used_font.size != device_font_size.into() ||
382 self.used_font.flags != flags;
383
384 self.used_font = FontInstance {
386 transform: font_transform,
387 size: device_font_size.into(),
388 flags,
389 ..specified_font.clone()
390 };
391
392 if !allow_subpixel || !use_subpixel_aa {
394 self.used_font.disable_subpixel_aa();
395
396 if oversized {
402 self.used_font.disable_subpixel_position();
403 }
404 }
405
406 cache_dirty
407 }
408
409 fn get_raster_space_for_prim(
415 &self,
416 prim_spatial_node_index: SpatialNodeIndex,
417 low_quality_pinch_zoom: bool,
418 device_pixel_scale: DevicePixelScale,
419 spatial_tree: &SpatialTree,
420 ) -> RasterSpace {
421 let prim_spatial_node = spatial_tree.get_spatial_node(prim_spatial_node_index);
422 if prim_spatial_node.is_ancestor_or_self_zooming {
423 if low_quality_pinch_zoom {
424 RasterSpace::Local(1.0)
431 } else {
432 let root_spatial_node_index = spatial_tree.root_reference_frame_index();
433
434 let scale_factors = spatial_tree
439 .get_relative_transform(prim_spatial_node_index, root_spatial_node_index)
440 .scale_factors();
441
442 let scale = scale_factors.0.max(scale_factors.1).min(8.0).max(1.0);
444 let rounded_up = 2.0f32.powf(scale.log2().ceil());
445
446 RasterSpace::Local(rounded_up / device_pixel_scale.0)
447 }
448 } else {
449 match self.requested_raster_space {
452 RasterSpace::Local(scale) => RasterSpace::Local(scale / device_pixel_scale.0),
453 RasterSpace::Screen => RasterSpace::Screen,
454 }
455 }
456 }
457
458 pub fn request_resources(
459 &mut self,
460 prim_offset: LayoutVector2D,
461 specified_font: &FontInstance,
462 glyphs: &[GlyphInstance],
463 transform: &LayoutToWorldTransform,
464 surface: &SurfaceInfo,
465 spatial_node_index: SpatialNodeIndex,
466 allow_subpixel: bool,
467 low_quality_pinch_zoom: bool,
468 resource_cache: &mut ResourceCache,
469 gpu_cache: &mut GpuCache,
470 spatial_tree: &SpatialTree,
471 scratch: &mut PrimitiveScratchBuffer,
472 ) {
473 let raster_space = self.get_raster_space_for_prim(
474 spatial_node_index,
475 low_quality_pinch_zoom,
476 surface.device_pixel_scale,
477 spatial_tree,
478 );
479
480 let cache_dirty = self.update_font_instance(
481 specified_font,
482 surface,
483 spatial_node_index,
484 transform,
485 allow_subpixel,
486 raster_space,
487 spatial_tree,
488 );
489
490 if self.glyph_keys_range.is_empty() || cache_dirty {
491 let subpx_dir = self.used_font.get_subpx_dir();
492
493 let dps = surface.device_pixel_scale.0;
494 let transform = match raster_space {
495 RasterSpace::Local(scale) => FontTransform::new(scale * dps, 0.0, 0.0, scale * dps),
496 RasterSpace::Screen => self.used_font.transform.scale(dps),
497 };
498
499 self.glyph_keys_range = scratch.glyph_keys.extend(
500 glyphs.iter().map(|src| {
501 let src_point = src.point + prim_offset;
502 let device_offset = transform.transform(&src_point);
503 GlyphKey::new(src.index, device_offset, subpx_dir)
504 }));
505 }
506
507 resource_cache.request_glyphs(
508 self.used_font.clone(),
509 &scratch.glyph_keys[self.glyph_keys_range],
510 gpu_cache,
511 );
512 }
513}
514
515#[test]
517#[cfg(target_os = "linux")]
518fn test_struct_sizes() {
519 use std::mem;
520 assert_eq!(mem::size_of::<TextRun>(), 88, "TextRun size changed");
527 assert_eq!(mem::size_of::<TextRunTemplate>(), 96, "TextRunTemplate size changed");
528 assert_eq!(mem::size_of::<TextRunKey>(), 104, "TextRunKey size changed");
529 assert_eq!(mem::size_of::<TextRunPrimitive>(), 80, "TextRunPrimitive size changed");
530}