Skip to content

Commit 2daf883

Browse files
authored
add text-sizes example (#114)
* add text-sizes example * rename window title in text-sizes example
1 parent b2129f1 commit 2daf883

File tree

1 file changed

+303
-0
lines changed

1 file changed

+303
-0
lines changed

examples/text-sizes.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
use glyphon::{
2+
Attrs, Buffer, Cache, Color, ColorMode, Family, FontSystem, Metrics, Resolution, Shaping,
3+
SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, Weight,
4+
};
5+
use std::sync::Arc;
6+
use wgpu::{
7+
CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Instance, InstanceDescriptor,
8+
LoadOp, MultisampleState, Operations, PresentMode, RenderPassColorAttachment,
9+
RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, TextureFormat,
10+
TextureUsages, TextureViewDescriptor,
11+
};
12+
use winit::{
13+
dpi::{LogicalSize, PhysicalSize},
14+
event::WindowEvent,
15+
event_loop::EventLoop,
16+
window::Window,
17+
};
18+
19+
const TEXT: &str = "The quick brown fox jumped over the lazy doggo. 🐕";
20+
const WEIGHT: Weight = Weight::NORMAL;
21+
const SIZES: [f32; 16] = [
22+
8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 28.0, 32.0, 48.0,
23+
];
24+
const LINE_HEIGHT: f32 = 1.15;
25+
const BG_COLOR: wgpu::Color = wgpu::Color::WHITE;
26+
const FONT_COLOR: Color = Color::rgb(0, 0, 0);
27+
//const BG_COLOR: wgpu::Color = wgpu::Color::BLACK;
28+
//const FONT_COLOR: Color = Color::rgb(255, 255, 255);
29+
const USE_WEB_COLORS: bool = true;
30+
31+
fn main() {
32+
let event_loop = EventLoop::new().unwrap();
33+
event_loop
34+
.run_app(&mut Application { window_state: None })
35+
.unwrap();
36+
}
37+
38+
struct WindowState {
39+
device: wgpu::Device,
40+
queue: wgpu::Queue,
41+
surface: wgpu::Surface<'static>,
42+
surface_config: SurfaceConfiguration,
43+
physical_size: PhysicalSize<i32>,
44+
scale_factor: f32,
45+
46+
font_system: FontSystem,
47+
swash_cache: SwashCache,
48+
viewport: glyphon::Viewport,
49+
atlas: glyphon::TextAtlas,
50+
text_renderer: glyphon::TextRenderer,
51+
buffers: Vec<glyphon::Buffer>,
52+
53+
// Make sure that the winit window is last in the struct so that
54+
// it is dropped after the wgpu surface is dropped, otherwise the
55+
// program may crash when closed. This is probably a bug in wgpu.
56+
window: Arc<Window>,
57+
}
58+
59+
impl WindowState {
60+
async fn new(window: Arc<Window>) -> Self {
61+
let physical_size = window.inner_size();
62+
let scale_factor = window.scale_factor() as f32;
63+
64+
// Set up surface
65+
let instance = Instance::new(InstanceDescriptor::default());
66+
let adapter = instance
67+
.request_adapter(&RequestAdapterOptions::default())
68+
.await
69+
.unwrap();
70+
let (device, queue) = adapter
71+
.request_device(&DeviceDescriptor::default(), None)
72+
.await
73+
.unwrap();
74+
75+
let (color_mode, swapchain_format) = if USE_WEB_COLORS {
76+
(ColorMode::Web, TextureFormat::Bgra8Unorm)
77+
} else {
78+
(ColorMode::Accurate, TextureFormat::Bgra8UnormSrgb)
79+
};
80+
81+
let surface = instance
82+
.create_surface(window.clone())
83+
.expect("Create surface");
84+
let surface_config = SurfaceConfiguration {
85+
usage: TextureUsages::RENDER_ATTACHMENT,
86+
format: swapchain_format,
87+
width: physical_size.width,
88+
height: physical_size.height,
89+
present_mode: PresentMode::Fifo,
90+
alpha_mode: CompositeAlphaMode::Opaque,
91+
view_formats: vec![],
92+
desired_maximum_frame_latency: 2,
93+
};
94+
surface.configure(&device, &surface_config);
95+
96+
let logical_width = physical_size.width as f32 / scale_factor;
97+
98+
// Set up text renderer
99+
let mut font_system = FontSystem::new();
100+
let swash_cache = SwashCache::new();
101+
let cache = Cache::new(&device);
102+
let viewport = Viewport::new(&device, &cache);
103+
let mut atlas =
104+
TextAtlas::with_color_mode(&device, &queue, &cache, swapchain_format, color_mode);
105+
let text_renderer =
106+
TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None);
107+
108+
let attrs = Attrs::new().family(Family::SansSerif).weight(WEIGHT);
109+
let shaping = Shaping::Advanced;
110+
111+
let buffers: Vec<glyphon::Buffer> = SIZES
112+
.iter()
113+
.copied()
114+
.map(|s| {
115+
let mut text_buffer =
116+
Buffer::new(&mut font_system, Metrics::relative(s, LINE_HEIGHT));
117+
118+
text_buffer.set_size(&mut font_system, Some(logical_width - 20.0), None);
119+
120+
text_buffer.set_text(
121+
&mut font_system,
122+
&format!("size {s}: {TEXT}"),
123+
attrs,
124+
shaping,
125+
);
126+
127+
text_buffer.shape_until_scroll(&mut font_system, false);
128+
129+
text_buffer
130+
})
131+
.collect();
132+
133+
Self {
134+
device,
135+
queue,
136+
surface,
137+
surface_config,
138+
physical_size: physical_size.cast(),
139+
scale_factor: scale_factor as f32,
140+
font_system,
141+
swash_cache,
142+
viewport,
143+
atlas,
144+
text_renderer,
145+
buffers,
146+
window,
147+
}
148+
}
149+
}
150+
151+
struct Application {
152+
window_state: Option<WindowState>,
153+
}
154+
155+
impl winit::application::ApplicationHandler for Application {
156+
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
157+
if self.window_state.is_some() {
158+
return;
159+
}
160+
161+
// Set up window
162+
let (width, height) = (800, 600);
163+
let window_attributes = Window::default_attributes()
164+
.with_inner_size(LogicalSize::new(width as f64, height as f64))
165+
.with_title("glyphon text sizes test");
166+
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
167+
168+
self.window_state = Some(pollster::block_on(WindowState::new(window)));
169+
}
170+
171+
fn window_event(
172+
&mut self,
173+
event_loop: &winit::event_loop::ActiveEventLoop,
174+
_window_id: winit::window::WindowId,
175+
event: WindowEvent,
176+
) {
177+
let Some(state) = &mut self.window_state else {
178+
return;
179+
};
180+
181+
let WindowState {
182+
window,
183+
device,
184+
queue,
185+
surface,
186+
surface_config,
187+
font_system,
188+
swash_cache,
189+
viewport,
190+
atlas,
191+
text_renderer,
192+
buffers,
193+
scale_factor,
194+
physical_size,
195+
..
196+
} = state;
197+
198+
match event {
199+
WindowEvent::Resized(size) => {
200+
surface_config.width = size.width;
201+
surface_config.height = size.height;
202+
surface.configure(&device, &surface_config);
203+
window.request_redraw();
204+
205+
*scale_factor = window.scale_factor() as f32;
206+
*physical_size = size.cast();
207+
208+
let logical_width = size.width as f32 / *scale_factor;
209+
210+
for b in buffers.iter_mut() {
211+
b.set_size(font_system, Some(logical_width - 20.0), None);
212+
b.shape_until_scroll(font_system, false);
213+
}
214+
}
215+
WindowEvent::RedrawRequested => {
216+
viewport.update(
217+
&queue,
218+
Resolution {
219+
width: surface_config.width,
220+
height: surface_config.height,
221+
},
222+
);
223+
224+
let scale_factor = *scale_factor;
225+
226+
let left = 10.0 * scale_factor;
227+
let mut top = 10.0 * scale_factor;
228+
229+
let bounds_left = left.floor() as i32;
230+
let bounds_right = physical_size.width - 10;
231+
232+
let text_areas: Vec<TextArea> = buffers
233+
.iter()
234+
.map(|b| {
235+
let a = TextArea {
236+
buffer: b,
237+
left,
238+
top,
239+
scale: scale_factor,
240+
bounds: TextBounds {
241+
left: bounds_left,
242+
top: top.floor() as i32,
243+
right: bounds_right,
244+
bottom: top.floor() as i32 + physical_size.height,
245+
},
246+
default_color: FONT_COLOR,
247+
};
248+
249+
let total_lines = b
250+
.layout_runs()
251+
.fold(0usize, |total_lines, _| total_lines + 1);
252+
253+
top += (total_lines as f32 * b.metrics().line_height + 5.0) * scale_factor;
254+
255+
a
256+
})
257+
.collect();
258+
259+
text_renderer
260+
.prepare(
261+
device,
262+
queue,
263+
font_system,
264+
atlas,
265+
viewport,
266+
text_areas,
267+
swash_cache,
268+
)
269+
.unwrap();
270+
271+
let frame = surface.get_current_texture().unwrap();
272+
let view = frame.texture.create_view(&TextureViewDescriptor::default());
273+
let mut encoder =
274+
device.create_command_encoder(&CommandEncoderDescriptor { label: None });
275+
{
276+
let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {
277+
label: None,
278+
color_attachments: &[Some(RenderPassColorAttachment {
279+
view: &view,
280+
resolve_target: None,
281+
ops: Operations {
282+
load: LoadOp::Clear(BG_COLOR),
283+
store: wgpu::StoreOp::Store,
284+
},
285+
})],
286+
depth_stencil_attachment: None,
287+
timestamp_writes: None,
288+
occlusion_query_set: None,
289+
});
290+
291+
text_renderer.render(&atlas, &viewport, &mut pass).unwrap();
292+
}
293+
294+
queue.submit(Some(encoder.finish()));
295+
frame.present();
296+
297+
atlas.trim();
298+
}
299+
WindowEvent::CloseRequested => event_loop.exit(),
300+
_ => {}
301+
}
302+
}
303+
}

0 commit comments

Comments
 (0)