diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 580c434..7e39b36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,8 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - name: Build - run: cargo build --all-targets --verbose + run: cargo build --verbose - name: Run tests run: cargo test --verbose diff --git a/Cargo.toml b/Cargo.toml index ea27abb..8b158ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,18 @@ [package] name = "glyphon" description = "Fast, simple 2D text rendering for wgpu" -version = "0.6.0" +version = "0.5.0" edition = "2021" homepage = "https://github.com/grovesNL/glyphon.git" repository = "https://github.com/grovesNL/glyphon" license = "MIT OR Apache-2.0 OR Zlib" [dependencies] -wgpu = { version = "22", default-features = false, features = ["wgsl"] } +wgpu = "0.19" etagere = "0.2.10" -cosmic-text = "0.12" -lru = { version = "0.12.1", default-features = false } -rustc-hash = "2.0" +lru = "0.12.1" +cosmic-text = "0.11.2" [dev-dependencies] -winit = "0.30.3" -wgpu = "22" -resvg = { version = "0.42", default-features = false } +winit = { version = "0.29.10", features = ["rwh_05"] } pollster = "0.3.0" diff --git a/examples/custom-glyphs.rs b/examples/custom-glyphs.rs deleted file mode 100644 index 98f6616..0000000 --- a/examples/custom-glyphs.rs +++ /dev/null @@ -1,330 +0,0 @@ -use glyphon::{ - Attrs, Buffer, Cache, Color, ContentType, CustomGlyph, Family, FontSystem, Metrics, - RasterizeCustomGlyphRequest, RasterizedCustomGlyph, Resolution, Shaping, SwashCache, TextArea, - TextAtlas, TextBounds, TextRenderer, Viewport, -}; -use std::sync::Arc; -use wgpu::{ - CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Instance, InstanceDescriptor, - LoadOp, MultisampleState, Operations, PresentMode, RenderPassColorAttachment, - RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, TextureFormat, - TextureUsages, TextureViewDescriptor, -}; -use winit::{dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, window::Window}; - -// Example SVG icons are from https://publicdomainvectors.org/ -static LION_SVG: &[u8] = include_bytes!("./lion.svg"); -static EAGLE_SVG: &[u8] = include_bytes!("./eagle.svg"); - -fn main() { - let event_loop = EventLoop::new().unwrap(); - event_loop - .run_app(&mut Application { window_state: None }) - .unwrap(); -} - -struct WindowState { - device: wgpu::Device, - queue: wgpu::Queue, - surface: wgpu::Surface<'static>, - surface_config: SurfaceConfiguration, - font_system: FontSystem, - swash_cache: SwashCache, - viewport: glyphon::Viewport, - atlas: glyphon::TextAtlas, - text_renderer: glyphon::TextRenderer, - text_buffer: glyphon::Buffer, - rasterize_svg: Box Option>, - // Make sure that the winit window is last in the struct so that - // it is dropped after the wgpu surface is dropped, otherwise the - // program may crash when closed. This is probably a bug in wgpu. - window: Arc, -} - -impl WindowState { - async fn new(window: Arc) -> Self { - let physical_size = window.inner_size(); - let scale_factor = window.scale_factor(); - - // Set up surface - let instance = Instance::new(InstanceDescriptor::default()); - let adapter = instance - .request_adapter(&RequestAdapterOptions::default()) - .await - .unwrap(); - let (device, queue) = adapter - .request_device(&DeviceDescriptor::default(), None) - .await - .unwrap(); - - let surface = instance - .create_surface(window.clone()) - .expect("Create surface"); - let swapchain_format = TextureFormat::Bgra8UnormSrgb; - let surface_config = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: physical_size.width, - height: physical_size.height, - present_mode: PresentMode::Fifo, - alpha_mode: CompositeAlphaMode::Opaque, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - surface.configure(&device, &surface_config); - - // Set up text renderer - let mut font_system = FontSystem::new(); - let swash_cache = SwashCache::new(); - let cache = Cache::new(&device); - let viewport = Viewport::new(&device, &cache); - let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); - let text_renderer = - TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); - let mut text_buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0)); - - let physical_width = (physical_size.width as f64 * scale_factor) as f32; - let physical_height = (physical_size.height as f64 * scale_factor) as f32; - - text_buffer.set_size( - &mut font_system, - Some(physical_width), - Some(physical_height), - ); - text_buffer.set_text( - &mut font_system, - "SVG icons! --->\n\nThe icons below should be partially clipped.", - Attrs::new().family(Family::SansSerif), - Shaping::Advanced, - ); - text_buffer.shape_until_scroll(&mut font_system, false); - - // Set up custom svg renderer - let svg_0 = resvg::usvg::Tree::from_data(LION_SVG, &Default::default()).unwrap(); - let svg_1 = resvg::usvg::Tree::from_data(EAGLE_SVG, &Default::default()).unwrap(); - - let rasterize_svg = - move |input: RasterizeCustomGlyphRequest| -> Option { - // Select the svg data based on the custom glyph ID. - let (svg, content_type) = match input.id { - 0 => (&svg_0, ContentType::Mask), - 1 => (&svg_1, ContentType::Color), - _ => return None, - }; - - // Calculate the scale based on the "glyph size". - let svg_size = svg.size(); - let scale_x = input.width as f32 / svg_size.width(); - let scale_y = input.height as f32 / svg_size.height(); - - let Some(mut pixmap) = - resvg::tiny_skia::Pixmap::new(input.width as u32, input.height as u32) - else { - return None; - }; - - let mut transform = resvg::usvg::Transform::from_scale(scale_x, scale_y); - - // Offset the glyph by the subpixel amount. - let offset_x = input.x_bin.as_float(); - let offset_y = input.y_bin.as_float(); - if offset_x != 0.0 || offset_y != 0.0 { - transform = transform.post_translate(offset_x, offset_y); - } - - resvg::render(svg, transform, &mut pixmap.as_mut()); - - let data: Vec = if let ContentType::Mask = content_type { - // Only use the alpha channel for symbolic icons. - pixmap.data().iter().skip(3).step_by(4).copied().collect() - } else { - pixmap.data().to_vec() - }; - - Some(RasterizedCustomGlyph { data, content_type }) - }; - - Self { - device, - queue, - surface, - surface_config, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - text_buffer, - rasterize_svg: Box::new(rasterize_svg), - window, - } - } -} - -struct Application { - window_state: Option, -} - -impl winit::application::ApplicationHandler for Application { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.window_state.is_some() { - return; - } - - // Set up window - let (width, height) = (800, 600); - let window_attributes = Window::default_attributes() - .with_inner_size(LogicalSize::new(width as f64, height as f64)) - .with_title("glyphon hello world"); - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - self.window_state = Some(pollster::block_on(WindowState::new(window))); - } - - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - _window_id: winit::window::WindowId, - event: WindowEvent, - ) { - let Some(state) = &mut self.window_state else { - return; - }; - - let WindowState { - window, - device, - queue, - surface, - surface_config, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - text_buffer, - rasterize_svg, - .. - } = state; - - match event { - WindowEvent::Resized(size) => { - surface_config.width = size.width; - surface_config.height = size.height; - surface.configure(&device, &surface_config); - window.request_redraw(); - } - WindowEvent::RedrawRequested => { - viewport.update( - &queue, - Resolution { - width: surface_config.width, - height: surface_config.height, - }, - ); - - text_renderer - .prepare_with_custom( - device, - queue, - font_system, - atlas, - viewport, - [TextArea { - buffer: &text_buffer, - left: 10.0, - top: 10.0, - scale: 1.0, - bounds: TextBounds { - left: 0, - top: 0, - right: 650, - bottom: 180, - }, - default_color: Color::rgb(255, 255, 255), - custom_glyphs: &[ - CustomGlyph { - id: 0, - left: 300.0, - top: 5.0, - width: 64.0, - height: 64.0, - color: Some(Color::rgb(200, 200, 255)), - snap_to_physical_pixel: true, - metadata: 0, - }, - CustomGlyph { - id: 1, - left: 400.0, - top: 5.0, - width: 64.0, - height: 64.0, - color: None, - snap_to_physical_pixel: true, - metadata: 0, - }, - CustomGlyph { - id: 0, - left: 300.0, - top: 130.0, - width: 64.0, - height: 64.0, - color: Some(Color::rgb(200, 255, 200)), - snap_to_physical_pixel: true, - metadata: 0, - }, - CustomGlyph { - id: 1, - left: 400.0, - top: 130.0, - width: 64.0, - height: 64.0, - color: None, - snap_to_physical_pixel: true, - metadata: 0, - }, - ], - }], - swash_cache, - rasterize_svg, - ) - .unwrap(); - - let frame = surface.get_current_texture().unwrap(); - let view = frame.texture.create_view(&TextureViewDescriptor::default()); - let mut encoder = - device.create_command_encoder(&CommandEncoderDescriptor { label: None }); - { - let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(wgpu::Color { - r: 0.02, - g: 0.02, - b: 0.02, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - text_renderer.render(&atlas, &viewport, &mut pass).unwrap(); - } - - queue.submit(Some(encoder.finish())); - frame.present(); - - atlas.trim(); - } - WindowEvent::CloseRequested => event_loop.exit(), - _ => {} - } - } -} diff --git a/examples/eagle.svg b/examples/eagle.svg deleted file mode 100644 index 53ad249..0000000 --- a/examples/eagle.svg +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - Eagle - 2007-01-24T06:25:54 - animal, animal, bird, bird, clip art, clipart, eagle, eagle, head, head, image, media, nature, nature, public domain, svg, - http://openclipart.org/detail/2962/eagle-by-nfroidure - - - nfroidure - - - - - animal - bird - clip art - clipart - eagle - head - image - media - nature - public domain - svg - - - - - - - - - - - diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 51c5c49..daafbb3 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -1,227 +1,159 @@ use glyphon::{ - Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, - TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, + Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea, + TextAtlas, TextBounds, TextRenderer, }; -use std::sync::Arc; use wgpu::{ - CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Instance, InstanceDescriptor, - LoadOp, MultisampleState, Operations, PresentMode, RenderPassColorAttachment, - RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, TextureFormat, - TextureUsages, TextureViewDescriptor, + CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance, + InstanceDescriptor, Limits, LoadOp, MultisampleState, Operations, PresentMode, + RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, + TextureFormat, TextureUsages, TextureViewDescriptor, }; -use winit::{dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, window::Window}; +use winit::{ + dpi::LogicalSize, + event::{Event, WindowEvent}, + event_loop::EventLoop, + window::WindowBuilder, +}; + +use std::sync::Arc; fn main() { + pollster::block_on(run()); +} + +async fn run() { + // Set up window + let (width, height) = (800, 600); let event_loop = EventLoop::new().unwrap(); + let window = Arc::new(WindowBuilder::new() + .with_inner_size(LogicalSize::new(width as f64, height as f64)) + .with_title("glyphon hello world") + .build(&event_loop) + .unwrap()); + let size = window.inner_size(); + let scale_factor = window.scale_factor(); + + // Set up surface + let instance = Instance::new(InstanceDescriptor::default()); + let adapter = instance + .request_adapter(&RequestAdapterOptions::default()) + .await + .unwrap(); + let (device, queue) = adapter + .request_device( + &DeviceDescriptor { + label: None, + required_features: Features::empty(), + required_limits: Limits::downlevel_defaults(), + }, + None, + ) + .await + .unwrap(); + + let surface = instance.create_surface(window.clone()).expect("Create surface"); + let swapchain_format = TextureFormat::Bgra8UnormSrgb; + let mut config = SurfaceConfiguration { + usage: TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: PresentMode::Fifo, + alpha_mode: CompositeAlphaMode::Opaque, + view_formats: vec![], + desired_maximum_frame_latency: 2, + }; + surface.configure(&device, &config); + + // Set up text renderer + let mut font_system = FontSystem::new(); + let mut cache = SwashCache::new(); + let mut atlas = TextAtlas::new(&device, &queue, swapchain_format); + let mut text_renderer = + TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); + let mut buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0)); + + let physical_width = (width as f64 * scale_factor) as f32; + let physical_height = (height as f64 * scale_factor) as f32; + + buffer.set_size(&mut font_system, physical_width, physical_height); + buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); + buffer.shape_until_scroll(&mut font_system); + event_loop - .run_app(&mut Application { window_state: None }) + .run(move |event, target| { + if let Event::WindowEvent { + window_id: _, + event, + } = event + { + match event { + WindowEvent::Resized(size) => { + config.width = size.width; + config.height = size.height; + surface.configure(&device, &config); + window.request_redraw(); + } + WindowEvent::RedrawRequested => { + text_renderer + .prepare( + &device, + &queue, + &mut font_system, + &mut atlas, + Resolution { + width: config.width, + height: config.height, + }, + [TextArea { + buffer: &buffer, + left: 10.0, + top: 10.0, + scale: 1.0, + bounds: TextBounds { + left: 0, + top: 0, + right: 600, + bottom: 160, + }, + default_color: Color::rgb(255, 255, 255), + }], + &mut cache, + ) + .unwrap(); + + let frame = surface.get_current_texture().unwrap(); + let view = frame.texture.create_view(&TextureViewDescriptor::default()); + let mut encoder = device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { + label: None, + color_attachments: &[Some(RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + text_renderer.render(&atlas, &mut pass).unwrap(); + } + + queue.submit(Some(encoder.finish())); + frame.present(); + + atlas.trim(); + } + WindowEvent::CloseRequested => target.exit(), + _ => {} + } + } + }) .unwrap(); } - -struct WindowState { - device: wgpu::Device, - queue: wgpu::Queue, - surface: wgpu::Surface<'static>, - surface_config: SurfaceConfiguration, - - font_system: FontSystem, - swash_cache: SwashCache, - viewport: glyphon::Viewport, - atlas: glyphon::TextAtlas, - text_renderer: glyphon::TextRenderer, - text_buffer: glyphon::Buffer, - - // Make sure that the winit window is last in the struct so that - // it is dropped after the wgpu surface is dropped, otherwise the - // program may crash when closed. This is probably a bug in wgpu. - window: Arc, -} - -impl WindowState { - async fn new(window: Arc) -> Self { - let physical_size = window.inner_size(); - let scale_factor = window.scale_factor(); - - // Set up surface - let instance = Instance::new(InstanceDescriptor::default()); - let adapter = instance - .request_adapter(&RequestAdapterOptions::default()) - .await - .unwrap(); - let (device, queue) = adapter - .request_device(&DeviceDescriptor::default(), None) - .await - .unwrap(); - - let surface = instance - .create_surface(window.clone()) - .expect("Create surface"); - let swapchain_format = TextureFormat::Bgra8UnormSrgb; - let surface_config = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: physical_size.width, - height: physical_size.height, - present_mode: PresentMode::Fifo, - alpha_mode: CompositeAlphaMode::Opaque, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - surface.configure(&device, &surface_config); - - // Set up text renderer - let mut font_system = FontSystem::new(); - let swash_cache = SwashCache::new(); - let cache = Cache::new(&device); - let viewport = Viewport::new(&device, &cache); - let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); - let text_renderer = - TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); - let mut text_buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0)); - - let physical_width = (physical_size.width as f64 * scale_factor) as f32; - let physical_height = (physical_size.height as f64 * scale_factor) as f32; - - text_buffer.set_size( - &mut font_system, - Some(physical_width), - Some(physical_height), - ); - text_buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); - text_buffer.shape_until_scroll(&mut font_system, false); - - Self { - device, - queue, - surface, - surface_config, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - text_buffer, - window, - } - } -} - -struct Application { - window_state: Option, -} - -impl winit::application::ApplicationHandler for Application { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.window_state.is_some() { - return; - } - - // Set up window - let (width, height) = (800, 600); - let window_attributes = Window::default_attributes() - .with_inner_size(LogicalSize::new(width as f64, height as f64)) - .with_title("glyphon hello world"); - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - self.window_state = Some(pollster::block_on(WindowState::new(window))); - } - - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - _window_id: winit::window::WindowId, - event: WindowEvent, - ) { - let Some(state) = &mut self.window_state else { - return; - }; - - let WindowState { - window, - device, - queue, - surface, - surface_config, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - text_buffer, - .. - } = state; - - match event { - WindowEvent::Resized(size) => { - surface_config.width = size.width; - surface_config.height = size.height; - surface.configure(&device, &surface_config); - window.request_redraw(); - } - WindowEvent::RedrawRequested => { - viewport.update( - &queue, - Resolution { - width: surface_config.width, - height: surface_config.height, - }, - ); - - text_renderer - .prepare( - device, - queue, - font_system, - atlas, - viewport, - [TextArea { - buffer: text_buffer, - left: 10.0, - top: 10.0, - scale: 1.0, - bounds: TextBounds { - left: 0, - top: 0, - right: 600, - bottom: 160, - }, - default_color: Color::rgb(255, 255, 255), - custom_glyphs: &[], - }], - swash_cache, - ) - .unwrap(); - - let frame = surface.get_current_texture().unwrap(); - let view = frame.texture.create_view(&TextureViewDescriptor::default()); - let mut encoder = - device.create_command_encoder(&CommandEncoderDescriptor { label: None }); - { - let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - text_renderer.render(&atlas, &viewport, &mut pass).unwrap(); - } - - queue.submit(Some(encoder.finish())); - frame.present(); - - atlas.trim(); - } - WindowEvent::CloseRequested => event_loop.exit(), - _ => {} - } - } -} diff --git a/examples/lion.svg b/examples/lion.svg deleted file mode 100644 index 6fbde49..0000000 --- a/examples/lion.svg +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - - - - - - - - - diff --git a/examples/text-sizes.rs b/examples/text-sizes.rs deleted file mode 100644 index 6296a75..0000000 --- a/examples/text-sizes.rs +++ /dev/null @@ -1,304 +0,0 @@ -use glyphon::{ - Attrs, Buffer, Cache, Color, ColorMode, Family, FontSystem, Metrics, Resolution, Shaping, - SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, Weight, -}; -use std::sync::Arc; -use wgpu::{ - CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Instance, InstanceDescriptor, - LoadOp, MultisampleState, Operations, PresentMode, RenderPassColorAttachment, - RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, TextureFormat, - TextureUsages, TextureViewDescriptor, -}; -use winit::{ - dpi::{LogicalSize, PhysicalSize}, - event::WindowEvent, - event_loop::EventLoop, - window::Window, -}; - -const TEXT: &str = "The quick brown fox jumped over the lazy doggo. 🐕"; -const WEIGHT: Weight = Weight::NORMAL; -const SIZES: [f32; 16] = [ - 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, -]; -const LINE_HEIGHT: f32 = 1.15; -const BG_COLOR: wgpu::Color = wgpu::Color::WHITE; -const FONT_COLOR: Color = Color::rgb(0, 0, 0); -//const BG_COLOR: wgpu::Color = wgpu::Color::BLACK; -//const FONT_COLOR: Color = Color::rgb(255, 255, 255); -const USE_WEB_COLORS: bool = true; - -fn main() { - let event_loop = EventLoop::new().unwrap(); - event_loop - .run_app(&mut Application { window_state: None }) - .unwrap(); -} - -struct WindowState { - device: wgpu::Device, - queue: wgpu::Queue, - surface: wgpu::Surface<'static>, - surface_config: SurfaceConfiguration, - physical_size: PhysicalSize, - scale_factor: f32, - - font_system: FontSystem, - swash_cache: SwashCache, - viewport: glyphon::Viewport, - atlas: glyphon::TextAtlas, - text_renderer: glyphon::TextRenderer, - buffers: Vec, - - // Make sure that the winit window is last in the struct so that - // it is dropped after the wgpu surface is dropped, otherwise the - // program may crash when closed. This is probably a bug in wgpu. - window: Arc, -} - -impl WindowState { - async fn new(window: Arc) -> Self { - let physical_size = window.inner_size(); - let scale_factor = window.scale_factor() as f32; - - // Set up surface - let instance = Instance::new(InstanceDescriptor::default()); - let adapter = instance - .request_adapter(&RequestAdapterOptions::default()) - .await - .unwrap(); - let (device, queue) = adapter - .request_device(&DeviceDescriptor::default(), None) - .await - .unwrap(); - - let (color_mode, swapchain_format) = if USE_WEB_COLORS { - (ColorMode::Web, TextureFormat::Bgra8Unorm) - } else { - (ColorMode::Accurate, TextureFormat::Bgra8UnormSrgb) - }; - - let surface = instance - .create_surface(window.clone()) - .expect("Create surface"); - let surface_config = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: physical_size.width, - height: physical_size.height, - present_mode: PresentMode::Fifo, - alpha_mode: CompositeAlphaMode::Opaque, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - surface.configure(&device, &surface_config); - - let logical_width = physical_size.width as f32 / scale_factor; - - // Set up text renderer - let mut font_system = FontSystem::new(); - let swash_cache = SwashCache::new(); - let cache = Cache::new(&device); - let viewport = Viewport::new(&device, &cache); - let mut atlas = - TextAtlas::with_color_mode(&device, &queue, &cache, swapchain_format, color_mode); - let text_renderer = - TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); - - let attrs = Attrs::new().family(Family::SansSerif).weight(WEIGHT); - let shaping = Shaping::Advanced; - - let buffers: Vec = SIZES - .iter() - .copied() - .map(|s| { - let mut text_buffer = - Buffer::new(&mut font_system, Metrics::relative(s, LINE_HEIGHT)); - - text_buffer.set_size(&mut font_system, Some(logical_width - 20.0), None); - - text_buffer.set_text( - &mut font_system, - &format!("size {s}: {TEXT}"), - attrs, - shaping, - ); - - text_buffer.shape_until_scroll(&mut font_system, false); - - text_buffer - }) - .collect(); - - Self { - device, - queue, - surface, - surface_config, - physical_size: physical_size.cast(), - scale_factor: scale_factor as f32, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - buffers, - window, - } - } -} - -struct Application { - window_state: Option, -} - -impl winit::application::ApplicationHandler for Application { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.window_state.is_some() { - return; - } - - // Set up window - let (width, height) = (800, 600); - let window_attributes = Window::default_attributes() - .with_inner_size(LogicalSize::new(width as f64, height as f64)) - .with_title("glyphon text sizes test"); - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - self.window_state = Some(pollster::block_on(WindowState::new(window))); - } - - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - _window_id: winit::window::WindowId, - event: WindowEvent, - ) { - let Some(state) = &mut self.window_state else { - return; - }; - - let WindowState { - window, - device, - queue, - surface, - surface_config, - font_system, - swash_cache, - viewport, - atlas, - text_renderer, - buffers, - scale_factor, - physical_size, - .. - } = state; - - match event { - WindowEvent::Resized(size) => { - surface_config.width = size.width; - surface_config.height = size.height; - surface.configure(&device, &surface_config); - window.request_redraw(); - - *scale_factor = window.scale_factor() as f32; - *physical_size = size.cast(); - - let logical_width = size.width as f32 / *scale_factor; - - for b in buffers.iter_mut() { - b.set_size(font_system, Some(logical_width - 20.0), None); - b.shape_until_scroll(font_system, false); - } - } - WindowEvent::RedrawRequested => { - viewport.update( - &queue, - Resolution { - width: surface_config.width, - height: surface_config.height, - }, - ); - - let scale_factor = *scale_factor; - - let left = 10.0 * scale_factor; - let mut top = 10.0 * scale_factor; - - let bounds_left = left.floor() as i32; - let bounds_right = physical_size.width - 10; - - let text_areas: Vec