Remove iced_glow, glyph-brush, and wgpu_glyph dependencies
This commit is contained in:
parent
573d27eb52
commit
b9a9576207
47 changed files with 30 additions and 3846 deletions
11
Cargo.toml
11
Cargo.toml
|
|
@ -14,19 +14,15 @@ categories = ["gui"]
|
|||
[features]
|
||||
default = ["wgpu"]
|
||||
# Enables the `Image` widget
|
||||
image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"]
|
||||
image = ["iced_wgpu?/image", "image_rs"]
|
||||
# Enables the `Svg` widget
|
||||
svg = ["iced_wgpu?/svg", "iced_glow?/svg"]
|
||||
svg = ["iced_wgpu?/svg"]
|
||||
# Enables the `Canvas` widget
|
||||
canvas = ["iced_graphics/canvas"]
|
||||
# Enables the `QRCode` widget
|
||||
qr_code = ["iced_graphics/qr_code"]
|
||||
# Enables the `iced_wgpu` renderer
|
||||
wgpu = ["iced_wgpu"]
|
||||
# Enables using system fonts
|
||||
default_system_font = ["iced_wgpu?/default_system_font", "iced_glow?/default_system_font"]
|
||||
# Enables the `iced_glow` renderer. Overrides `iced_wgpu`
|
||||
glow = ["iced_glow", "iced_glutin"]
|
||||
# Enables a debug view in native platforms (press F12)
|
||||
debug = ["iced_winit/debug"]
|
||||
# Enables `tokio` as the `executor::Default` on native platforms
|
||||
|
|
@ -44,7 +40,6 @@ chrome-trace = [
|
|||
"iced_winit/chrome-trace",
|
||||
"iced_glutin?/trace",
|
||||
"iced_wgpu?/tracing",
|
||||
"iced_glow?/tracing",
|
||||
]
|
||||
|
||||
[badges]
|
||||
|
|
@ -55,7 +50,6 @@ members = [
|
|||
"core",
|
||||
"futures",
|
||||
"graphics",
|
||||
"glow",
|
||||
"glutin",
|
||||
"lazy",
|
||||
"native",
|
||||
|
|
@ -72,7 +66,6 @@ iced_native = { version = "0.9", path = "native" }
|
|||
iced_graphics = { version = "0.7", path = "graphics" }
|
||||
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
|
||||
iced_glutin = { version = "0.7", path = "glutin", optional = true }
|
||||
iced_glow = { version = "0.7", path = "glow", optional = true }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dependencies.image_rs]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "integration_wgpu"
|
||||
name = "integration"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
<h1>integration_wgpu</h1>
|
||||
<canvas id="iced_canvas"></canvas>
|
||||
<script type="module">
|
||||
import init from "./integration_wgpu.js";
|
||||
init('./integration_wgpu_bg.wasm');
|
||||
import init from "./integration.js";
|
||||
init('./integration_bg.wasm');
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
[package]
|
||||
name = "integration_opengl"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced_glutin = { path = "../../glutin" }
|
||||
iced_glow = { path = "../../glow" }
|
||||
iced_winit = { path = "../../winit" }
|
||||
env_logger = "0.8"
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
## OpenGL integration
|
||||
|
||||
A demonstration of how to integrate Iced in an existing graphical OpenGL application.
|
||||
|
||||
The __[`main`]__ file contains all the code of the example.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://imgbox.com/9P9ETcod" target="_blank"><img src="https://images2.imgbox.com/2a/51/9P9ETcod_o.gif" alt="image host"/></a>
|
||||
</div>
|
||||
|
||||
You can run it with `cargo run`:
|
||||
```
|
||||
cargo run --package integration_opengl
|
||||
```
|
||||
|
||||
[`main`]: src/main.rs
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
use iced_glow::Renderer;
|
||||
use iced_glutin::widget::Slider;
|
||||
use iced_glutin::widget::{Column, Row, Text};
|
||||
use iced_glutin::{Alignment, Color, Command, Element, Length, Program};
|
||||
|
||||
pub struct Controls {
|
||||
background_color: Color,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
BackgroundColorChanged(Color),
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
pub fn new() -> Controls {
|
||||
Controls {
|
||||
background_color: Color::BLACK,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn background_color(&self) -> Color {
|
||||
self.background_color
|
||||
}
|
||||
}
|
||||
|
||||
impl Program for Controls {
|
||||
type Renderer = Renderer;
|
||||
type Message = Message;
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::BackgroundColorChanged(color) => {
|
||||
self.background_color = color;
|
||||
}
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message, Renderer> {
|
||||
let background_color = self.background_color;
|
||||
|
||||
let sliders = Row::new()
|
||||
.width(500)
|
||||
.spacing(20)
|
||||
.push(
|
||||
Slider::new(0.0..=1.0, background_color.r, move |r| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
r,
|
||||
..background_color
|
||||
})
|
||||
})
|
||||
.step(0.01),
|
||||
)
|
||||
.push(
|
||||
Slider::new(0.0..=1.0, background_color.g, move |g| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
g,
|
||||
..background_color
|
||||
})
|
||||
})
|
||||
.step(0.01),
|
||||
)
|
||||
.push(
|
||||
Slider::new(0.0..=1.0, background_color.b, move |b| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
b,
|
||||
..background_color
|
||||
})
|
||||
})
|
||||
.step(0.01),
|
||||
);
|
||||
|
||||
Row::new()
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.push(
|
||||
Text::new("Background color")
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{background_color:?}"))
|
||||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
mod controls;
|
||||
mod scene;
|
||||
|
||||
use controls::Controls;
|
||||
use scene::Scene;
|
||||
|
||||
use glow::*;
|
||||
use glutin::dpi::PhysicalPosition;
|
||||
use glutin::event::{Event, ModifiersState, WindowEvent};
|
||||
use glutin::event_loop::ControlFlow;
|
||||
use iced_glow::glow;
|
||||
use iced_glow::{Backend, Renderer, Settings, Viewport};
|
||||
use iced_glutin::conversion;
|
||||
use iced_glutin::glutin;
|
||||
use iced_glutin::renderer;
|
||||
use iced_glutin::{program, Clipboard, Color, Debug, Size};
|
||||
|
||||
pub fn main() {
|
||||
env_logger::init();
|
||||
let (gl, event_loop, windowed_context, shader_version) = {
|
||||
let el = glutin::event_loop::EventLoop::new();
|
||||
|
||||
let wb = glutin::window::WindowBuilder::new()
|
||||
.with_title("OpenGL integration example")
|
||||
.with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
|
||||
|
||||
let windowed_context = glutin::ContextBuilder::new()
|
||||
.with_vsync(true)
|
||||
.build_windowed(wb, &el)
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
let windowed_context = windowed_context.make_current().unwrap();
|
||||
|
||||
let gl = glow::Context::from_loader_function(|s| {
|
||||
windowed_context.get_proc_address(s) as *const _
|
||||
});
|
||||
|
||||
// Enable auto-conversion from/to sRGB
|
||||
gl.enable(glow::FRAMEBUFFER_SRGB);
|
||||
|
||||
// Enable alpha blending
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Disable multisampling by default
|
||||
gl.disable(glow::MULTISAMPLE);
|
||||
|
||||
(gl, el, windowed_context, "#version 410")
|
||||
}
|
||||
};
|
||||
|
||||
let physical_size = windowed_context.window().inner_size();
|
||||
let mut viewport = Viewport::with_physical_size(
|
||||
Size::new(physical_size.width, physical_size.height),
|
||||
windowed_context.window().scale_factor(),
|
||||
);
|
||||
|
||||
let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
|
||||
let mut modifiers = ModifiersState::default();
|
||||
let mut clipboard = Clipboard::connect(windowed_context.window());
|
||||
|
||||
let mut renderer = Renderer::new(Backend::new(&gl, Settings::default()));
|
||||
|
||||
let mut debug = Debug::new();
|
||||
|
||||
let controls = Controls::new();
|
||||
let mut state = program::State::new(
|
||||
controls,
|
||||
viewport.logical_size(),
|
||||
&mut renderer,
|
||||
&mut debug,
|
||||
);
|
||||
let mut resized = false;
|
||||
|
||||
let scene = Scene::new(&gl, shader_version);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
cursor_position = position;
|
||||
}
|
||||
WindowEvent::ModifiersChanged(new_modifiers) => {
|
||||
modifiers = new_modifiers;
|
||||
}
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
viewport = Viewport::with_physical_size(
|
||||
Size::new(
|
||||
physical_size.width,
|
||||
physical_size.height,
|
||||
),
|
||||
windowed_context.window().scale_factor(),
|
||||
);
|
||||
|
||||
resized = true;
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
scene.cleanup(&gl);
|
||||
*control_flow = ControlFlow::Exit
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// Map window event to iced event
|
||||
if let Some(event) = iced_winit::conversion::window_event(
|
||||
&event,
|
||||
windowed_context.window().scale_factor(),
|
||||
modifiers,
|
||||
) {
|
||||
state.queue_event(event);
|
||||
}
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
// If there are events pending
|
||||
if !state.is_queue_empty() {
|
||||
// We update iced
|
||||
let _ = state.update(
|
||||
viewport.logical_size(),
|
||||
conversion::cursor_position(
|
||||
cursor_position,
|
||||
viewport.scale_factor(),
|
||||
),
|
||||
&mut renderer,
|
||||
&iced_glow::Theme::Dark,
|
||||
&renderer::Style {
|
||||
text_color: Color::WHITE,
|
||||
},
|
||||
&mut clipboard,
|
||||
&mut debug,
|
||||
);
|
||||
|
||||
// and request a redraw
|
||||
windowed_context.window().request_redraw();
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
if resized {
|
||||
let size = windowed_context.window().inner_size();
|
||||
|
||||
unsafe {
|
||||
gl.viewport(
|
||||
0,
|
||||
0,
|
||||
size.width as i32,
|
||||
size.height as i32,
|
||||
);
|
||||
}
|
||||
|
||||
resized = false;
|
||||
}
|
||||
|
||||
let program = state.program();
|
||||
{
|
||||
// We clear the frame
|
||||
scene.clear(&gl, program.background_color());
|
||||
|
||||
// Draw the scene
|
||||
scene.draw(&gl);
|
||||
}
|
||||
|
||||
// And then iced on top
|
||||
renderer.with_primitives(|backend, primitive| {
|
||||
backend.present(
|
||||
&gl,
|
||||
primitive,
|
||||
&viewport,
|
||||
&debug.overlay(),
|
||||
);
|
||||
});
|
||||
|
||||
// Update the mouse cursor
|
||||
windowed_context.window().set_cursor_icon(
|
||||
iced_winit::conversion::mouse_interaction(
|
||||
state.mouse_interaction(),
|
||||
),
|
||||
);
|
||||
|
||||
windowed_context.swap_buffers().unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
use glow::*;
|
||||
use iced_glow::glow;
|
||||
use iced_glow::Color;
|
||||
|
||||
pub struct Scene {
|
||||
program: glow::Program,
|
||||
vertex_array: glow::VertexArray,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(gl: &glow::Context, shader_version: &str) -> Self {
|
||||
unsafe {
|
||||
let vertex_array = gl
|
||||
.create_vertex_array()
|
||||
.expect("Cannot create vertex array");
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
|
||||
let program = gl.create_program().expect("Cannot create program");
|
||||
|
||||
let (vertex_shader_source, fragment_shader_source) = (
|
||||
r#"const vec2 verts[3] = vec2[3](
|
||||
vec2(0.5f, 1.0f),
|
||||
vec2(0.0f, 0.0f),
|
||||
vec2(1.0f, 0.0f)
|
||||
);
|
||||
out vec2 vert;
|
||||
void main() {
|
||||
vert = verts[gl_VertexID];
|
||||
gl_Position = vec4(vert - 0.5, 0.0, 1.0);
|
||||
}"#,
|
||||
r#"precision highp float;
|
||||
in vec2 vert;
|
||||
out vec4 color;
|
||||
void main() {
|
||||
color = vec4(vert, 0.5, 1.0);
|
||||
}"#,
|
||||
);
|
||||
|
||||
let shader_sources = [
|
||||
(glow::VERTEX_SHADER, vertex_shader_source),
|
||||
(glow::FRAGMENT_SHADER, fragment_shader_source),
|
||||
];
|
||||
|
||||
let mut shaders = Vec::with_capacity(shader_sources.len());
|
||||
|
||||
for (shader_type, shader_source) in shader_sources.iter() {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
gl.shader_source(
|
||||
shader,
|
||||
&format!("{shader_version}\n{shader_source}"),
|
||||
);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
panic!("{}", gl.get_shader_info_log(shader));
|
||||
}
|
||||
gl.attach_shader(program, shader);
|
||||
shaders.push(shader);
|
||||
}
|
||||
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
panic!("{}", gl.get_program_info_log(program));
|
||||
}
|
||||
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader);
|
||||
gl.delete_shader(shader);
|
||||
}
|
||||
|
||||
gl.use_program(Some(program));
|
||||
Self {
|
||||
program,
|
||||
vertex_array,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self, gl: &glow::Context, background_color: Color) {
|
||||
let [r, g, b, a] = background_color.into_linear();
|
||||
unsafe {
|
||||
gl.clear_color(r, g, b, a);
|
||||
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, gl: &glow::Context) {
|
||||
unsafe {
|
||||
gl.bind_vertex_array(Some(self.vertex_array));
|
||||
gl.use_program(Some(self.program));
|
||||
gl.draw_arrays(glow::TRIANGLES, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cleanup(&self, gl: &glow::Context) {
|
||||
unsafe {
|
||||
gl.delete_program(self.program);
|
||||
gl.delete_vertex_array(self.vertex_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
[package]
|
||||
name = "iced_glow"
|
||||
version = "0.7.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A glow renderer for iced"
|
||||
license = "MIT AND OFL-1.1"
|
||||
repository = "https://github.com/iced-rs/iced"
|
||||
|
||||
[features]
|
||||
svg = ["iced_graphics/svg"]
|
||||
image = ["iced_graphics/image"]
|
||||
png = ["iced_graphics/png"]
|
||||
jpeg = ["iced_graphics/jpeg"]
|
||||
jpeg_rayon = ["iced_graphics/jpeg_rayon"]
|
||||
gif = ["iced_graphics/gif"]
|
||||
webp = ["iced_graphics/webp"]
|
||||
pnm = ["iced_graphics/pnm"]
|
||||
ico = ["iced_graphics/ico"]
|
||||
bmp = ["iced_graphics/bmp"]
|
||||
hdr = ["iced_graphics/hdr"]
|
||||
dds = ["iced_graphics/dds"]
|
||||
farbfeld = ["iced_graphics/farbfeld"]
|
||||
canvas = ["iced_graphics/canvas"]
|
||||
qr_code = ["iced_graphics/qr_code"]
|
||||
default_system_font = ["iced_graphics/font-source"]
|
||||
|
||||
[dependencies]
|
||||
glow = "0.11.1"
|
||||
glow_glyph = "0.5.0"
|
||||
glyph_brush = "0.7"
|
||||
euclid = "0.22"
|
||||
bytemuck = "1.4"
|
||||
log = "0.4"
|
||||
|
||||
[dependencies.iced_native]
|
||||
version = "0.9"
|
||||
path = "../native"
|
||||
|
||||
[dependencies.iced_graphics]
|
||||
version = "0.7"
|
||||
path = "../graphics"
|
||||
features = ["font-fallback", "font-icons", "opengl"]
|
||||
|
||||
[dependencies.tracing]
|
||||
version = "0.1.6"
|
||||
optional = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
all-features = true
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
# `iced_glow`
|
||||
[][documentation]
|
||||
[](https://crates.io/crates/iced_glow)
|
||||
[](https://github.com/iced-rs/iced/blob/master/LICENSE)
|
||||
[](https://discord.gg/3xZJ65GAhd)
|
||||
|
||||
`iced_glow` is a [`glow`] renderer for [`iced_native`]. This renderer supports OpenGL 3.0+ and OpenGL ES 2.0.
|
||||
|
||||
This renderer is mostly used as a fallback for hardware that doesn't support [`wgpu`] (Vulkan, Metal or DX12).
|
||||
|
||||
Currently, `iced_glow` supports the following primitives:
|
||||
- Text, which is rendered using [`glow_glyph`]. No shaping at all.
|
||||
- Quads or rectangles, with rounded borders and a solid background color.
|
||||
- Clip areas, useful to implement scrollables or hide overflowing content.
|
||||
- Meshes of triangles, useful to draw geometry freely.
|
||||
|
||||
<p align="center">
|
||||
<img alt="The native target" src="../docs/graphs/native.png" width="80%">
|
||||
</p>
|
||||
|
||||
[documentation]: https://docs.rs/iced_glow
|
||||
[`iced_native`]: ../native
|
||||
[`glow`]: https://github.com/grovesNL/glow
|
||||
[`wgpu`]: https://github.com/gfx-rs/wgpu
|
||||
[`glow_glyph`]: https://github.com/hecrj/glow_glyph
|
||||
|
||||
## Installation
|
||||
Add `iced_glow` as a dependency in your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
iced_glow = "0.7"
|
||||
```
|
||||
|
||||
__Iced moves fast and the `master` branch can contain breaking changes!__ If
|
||||
you want to learn about a specific release, check out [the release list].
|
||||
|
||||
[the release list]: https://github.com/iced-rs/iced/releases
|
||||
|
||||
## Current limitations
|
||||
|
||||
The current implementation is quite naive, it uses:
|
||||
|
||||
- A different pipeline/shader for each primitive
|
||||
- A very simplistic layer model: every `Clip` primitive will generate new layers
|
||||
- _Many_ render passes instead of preparing everything upfront
|
||||
- A glyph cache that is trimmed incorrectly when there are multiple layers (a [`glyph_brush`] limitation)
|
||||
|
||||
Some of these issues are already being worked on! If you want to help, [get in touch!]
|
||||
|
||||
[get in touch!]: ../CONTRIBUTING.md
|
||||
[`glyph_brush`]: https://github.com/alexheretic/glyph-brush
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
use crate::image;
|
||||
use crate::quad;
|
||||
use crate::text;
|
||||
use crate::{program, triangle};
|
||||
use crate::{Settings, Transformation, Viewport};
|
||||
|
||||
use iced_graphics::backend;
|
||||
use iced_graphics::font;
|
||||
use iced_graphics::{Layer, Primitive};
|
||||
use iced_native::alignment;
|
||||
use iced_native::{Font, Size};
|
||||
|
||||
/// A [`glow`] graphics backend for [`iced`].
|
||||
///
|
||||
/// [`glow`]: https://github.com/grovesNL/glow
|
||||
/// [`iced`]: https://github.com/iced-rs/iced
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
image_pipeline: image::Pipeline,
|
||||
quad_pipeline: quad::Pipeline,
|
||||
text_pipeline: text::Pipeline,
|
||||
triangle_pipeline: triangle::Pipeline,
|
||||
default_text_size: f32,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
/// Creates a new [`Backend`].
|
||||
pub fn new(gl: &glow::Context, settings: Settings) -> Self {
|
||||
let text_pipeline = text::Pipeline::new(
|
||||
gl,
|
||||
settings.default_font,
|
||||
settings.text_multithreading,
|
||||
);
|
||||
|
||||
let shader_version = program::Version::new(gl);
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
let image_pipeline = image::Pipeline::new(gl, &shader_version);
|
||||
let quad_pipeline = quad::Pipeline::new(gl, &shader_version);
|
||||
let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version);
|
||||
|
||||
Self {
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
image_pipeline,
|
||||
quad_pipeline,
|
||||
text_pipeline,
|
||||
triangle_pipeline,
|
||||
default_text_size: settings.default_text_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the provided primitives in the default framebuffer.
|
||||
///
|
||||
/// The text provided as overlay will be rendered on top of the primitives.
|
||||
/// This is useful for rendering debug information.
|
||||
pub fn present<T: AsRef<str>>(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
primitives: &[Primitive],
|
||||
viewport: &Viewport,
|
||||
overlay_text: &[T],
|
||||
) {
|
||||
let viewport_size = viewport.physical_size();
|
||||
let scale_factor = viewport.scale_factor() as f32;
|
||||
let projection = viewport.projection();
|
||||
|
||||
let mut layers = Layer::generate(primitives, viewport);
|
||||
layers.push(Layer::overlay(overlay_text, viewport));
|
||||
|
||||
for layer in layers {
|
||||
self.flush(
|
||||
gl,
|
||||
scale_factor,
|
||||
projection,
|
||||
&layer,
|
||||
viewport_size.height,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
self.image_pipeline.trim_cache(gl);
|
||||
}
|
||||
|
||||
fn flush(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
scale_factor: f32,
|
||||
transformation: Transformation,
|
||||
layer: &Layer<'_>,
|
||||
target_height: u32,
|
||||
) {
|
||||
let mut bounds = (layer.bounds * scale_factor).snap();
|
||||
|
||||
if bounds.width < 1 || bounds.height < 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
bounds.height = bounds.height.min(target_height);
|
||||
|
||||
if !layer.quads.is_empty() {
|
||||
self.quad_pipeline.draw(
|
||||
gl,
|
||||
target_height,
|
||||
&layer.quads,
|
||||
transformation,
|
||||
scale_factor,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
|
||||
if !layer.meshes.is_empty() {
|
||||
let scaled = transformation
|
||||
* Transformation::scale(scale_factor, scale_factor);
|
||||
|
||||
self.triangle_pipeline.draw(
|
||||
&layer.meshes,
|
||||
gl,
|
||||
target_height,
|
||||
scaled,
|
||||
scale_factor,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
if !layer.images.is_empty() {
|
||||
let scaled = transformation
|
||||
* Transformation::scale(scale_factor, scale_factor);
|
||||
|
||||
self.image_pipeline.draw(
|
||||
gl,
|
||||
target_height,
|
||||
scaled,
|
||||
scale_factor,
|
||||
&layer.images,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
for text in layer.text.iter() {
|
||||
// Target physical coordinates directly to avoid blurry text
|
||||
let text = glow_glyph::Section {
|
||||
// TODO: We `round` here to avoid rerasterizing text when
|
||||
// its position changes slightly. This can make text feel a
|
||||
// bit "jumpy". We may be able to do better once we improve
|
||||
// our text rendering/caching pipeline.
|
||||
screen_position: (
|
||||
(text.bounds.x * scale_factor).round(),
|
||||
(text.bounds.y * scale_factor).round(),
|
||||
),
|
||||
// TODO: Fix precision issues with some scale factors.
|
||||
//
|
||||
// The `ceil` here can cause some words to render on the
|
||||
// same line when they should not.
|
||||
//
|
||||
// Ideally, `wgpu_glyph` should be able to compute layout
|
||||
// using logical positions, and then apply the proper
|
||||
// scaling when rendering. This would ensure that both
|
||||
// measuring and rendering follow the same layout rules.
|
||||
bounds: (
|
||||
(text.bounds.width * scale_factor).ceil(),
|
||||
(text.bounds.height * scale_factor).ceil(),
|
||||
),
|
||||
text: vec![glow_glyph::Text {
|
||||
text: text.content,
|
||||
scale: glow_glyph::ab_glyph::PxScale {
|
||||
x: text.size * scale_factor,
|
||||
y: text.size * scale_factor,
|
||||
},
|
||||
font_id: self.text_pipeline.find_font(text.font),
|
||||
extra: glow_glyph::Extra {
|
||||
color: text.color,
|
||||
z: 0.0,
|
||||
},
|
||||
}],
|
||||
layout: glow_glyph::Layout::default()
|
||||
.h_align(match text.horizontal_alignment {
|
||||
alignment::Horizontal::Left => {
|
||||
glow_glyph::HorizontalAlign::Left
|
||||
}
|
||||
alignment::Horizontal::Center => {
|
||||
glow_glyph::HorizontalAlign::Center
|
||||
}
|
||||
alignment::Horizontal::Right => {
|
||||
glow_glyph::HorizontalAlign::Right
|
||||
}
|
||||
})
|
||||
.v_align(match text.vertical_alignment {
|
||||
alignment::Vertical::Top => {
|
||||
glow_glyph::VerticalAlign::Top
|
||||
}
|
||||
alignment::Vertical::Center => {
|
||||
glow_glyph::VerticalAlign::Center
|
||||
}
|
||||
alignment::Vertical::Bottom => {
|
||||
glow_glyph::VerticalAlign::Bottom
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
self.text_pipeline.queue(text);
|
||||
}
|
||||
|
||||
self.text_pipeline.draw_queued(
|
||||
gl,
|
||||
transformation,
|
||||
glow_glyph::Region {
|
||||
x: bounds.x,
|
||||
y: target_height - (bounds.y + bounds.height),
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl iced_graphics::Backend for Backend {
|
||||
fn trim_measurements(&mut self) {
|
||||
self.text_pipeline.trim_measurement_cache()
|
||||
}
|
||||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
const ICON_FONT: Font = font::ICONS;
|
||||
const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
|
||||
const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
|
||||
|
||||
fn default_size(&self) -> f32 {
|
||||
self.default_text_size
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&self,
|
||||
contents: &str,
|
||||
size: f32,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
) -> (f32, f32) {
|
||||
self.text_pipeline.measure(contents, size, font, bounds)
|
||||
}
|
||||
|
||||
fn hit_test(
|
||||
&self,
|
||||
contents: &str,
|
||||
size: f32,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
point: iced_native::Point,
|
||||
nearest_only: bool,
|
||||
) -> Option<text::Hit> {
|
||||
self.text_pipeline.hit_test(
|
||||
contents,
|
||||
size,
|
||||
font,
|
||||
bounds,
|
||||
point,
|
||||
nearest_only,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
impl backend::Image for Backend {
|
||||
fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
|
||||
self.image_pipeline.dimensions(handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
impl backend::Svg for Backend {
|
||||
fn viewport_dimensions(
|
||||
&self,
|
||||
handle: &iced_native::svg::Handle,
|
||||
) -> Size<u32> {
|
||||
self.image_pipeline.viewport_dimensions(handle)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
mod storage;
|
||||
|
||||
use storage::Storage;
|
||||
|
||||
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
||||
|
||||
use crate::program::{self, Shader};
|
||||
use crate::Transformation;
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
use iced_graphics::image::raster;
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
use iced_graphics::image::vector;
|
||||
|
||||
use iced_graphics::layer;
|
||||
use iced_graphics::Rectangle;
|
||||
use iced_graphics::Size;
|
||||
|
||||
use glow::HasContext;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
use tracing::info_span;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pipeline {
|
||||
program: <glow::Context as HasContext>::Program,
|
||||
vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||
vertex_buffer: <glow::Context as HasContext>::Buffer,
|
||||
transform_location: <glow::Context as HasContext>::UniformLocation,
|
||||
storage: Storage,
|
||||
#[cfg(feature = "image")]
|
||||
raster_cache: RefCell<raster::Cache<Storage>>,
|
||||
#[cfg(feature = "svg")]
|
||||
vector_cache: RefCell<vector::Cache<Storage>>,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
gl: &glow::Context,
|
||||
shader_version: &program::Version,
|
||||
) -> Pipeline {
|
||||
let program = unsafe {
|
||||
let vertex_shader = Shader::vertex(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/image.vert"),
|
||||
);
|
||||
let fragment_shader = Shader::fragment(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/image.frag"),
|
||||
);
|
||||
|
||||
program::create(
|
||||
gl,
|
||||
&[vertex_shader, fragment_shader],
|
||||
&[(0, "i_Position")],
|
||||
)
|
||||
};
|
||||
|
||||
let transform_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||
.expect("Get transform location");
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(program));
|
||||
|
||||
let transform: [f32; 16] = Transformation::identity().into();
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&transform_location),
|
||||
false,
|
||||
&transform,
|
||||
);
|
||||
|
||||
gl.use_program(None);
|
||||
}
|
||||
|
||||
let vertex_buffer =
|
||||
unsafe { gl.create_buffer().expect("Create vertex buffer") };
|
||||
let vertex_array =
|
||||
unsafe { gl.create_vertex_array().expect("Create vertex array") };
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
|
||||
|
||||
let vertices = &[0u8, 0, 1, 0, 0, 1, 1, 1];
|
||||
gl.buffer_data_size(
|
||||
glow::ARRAY_BUFFER,
|
||||
vertices.len() as i32,
|
||||
glow::STATIC_DRAW,
|
||||
);
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
0,
|
||||
bytemuck::cast_slice(vertices),
|
||||
);
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
0,
|
||||
2,
|
||||
glow::UNSIGNED_BYTE,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
gl.bind_vertex_array(None);
|
||||
}
|
||||
|
||||
Pipeline {
|
||||
program,
|
||||
vertex_array,
|
||||
vertex_buffer,
|
||||
transform_location,
|
||||
storage: Storage::default(),
|
||||
#[cfg(feature = "image")]
|
||||
raster_cache: RefCell::new(raster::Cache::default()),
|
||||
#[cfg(feature = "svg")]
|
||||
vector_cache: RefCell::new(vector::Cache::default()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
pub fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
|
||||
self.raster_cache.borrow_mut().load(handle).dimensions()
|
||||
}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
pub fn viewport_dimensions(
|
||||
&self,
|
||||
handle: &iced_native::svg::Handle,
|
||||
) -> Size<u32> {
|
||||
let mut cache = self.vector_cache.borrow_mut();
|
||||
let svg = cache.load(handle);
|
||||
|
||||
svg.viewport_dimensions()
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
mut gl: &glow::Context,
|
||||
target_height: u32,
|
||||
transformation: Transformation,
|
||||
_scale_factor: f32,
|
||||
images: &[layer::Image],
|
||||
layer_bounds: Rectangle<u32>,
|
||||
) {
|
||||
#[cfg(feature = "tracing")]
|
||||
let _ = info_span!("Glow::Image", "DRAW").entered();
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(self.program));
|
||||
gl.bind_vertex_array(Some(self.vertex_array));
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
let mut raster_cache = self.raster_cache.borrow_mut();
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
let mut vector_cache = self.vector_cache.borrow_mut();
|
||||
|
||||
for image in images {
|
||||
let (entry, bounds) = match &image {
|
||||
#[cfg(feature = "image")]
|
||||
layer::Image::Raster { handle, bounds } => (
|
||||
raster_cache.upload(handle, &mut gl, &mut self.storage),
|
||||
bounds,
|
||||
),
|
||||
#[cfg(not(feature = "image"))]
|
||||
layer::Image::Raster { handle: _, bounds } => (None, bounds),
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
layer::Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds,
|
||||
} => {
|
||||
let size = [bounds.width, bounds.height];
|
||||
(
|
||||
vector_cache.upload(
|
||||
handle,
|
||||
*color,
|
||||
size,
|
||||
_scale_factor,
|
||||
&mut gl,
|
||||
&mut self.storage,
|
||||
),
|
||||
bounds,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "svg"))]
|
||||
layer::Image::Vector { bounds, .. } => (None, bounds),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gl.scissor(
|
||||
layer_bounds.x as i32,
|
||||
(target_height - (layer_bounds.y + layer_bounds.height))
|
||||
as i32,
|
||||
layer_bounds.width as i32,
|
||||
layer_bounds.height as i32,
|
||||
);
|
||||
|
||||
if let Some(storage::Entry { texture, .. }) = entry {
|
||||
gl.bind_texture(glow::TEXTURE_2D, Some(*texture))
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let translate = Transformation::translate(bounds.x, bounds.y);
|
||||
let scale = Transformation::scale(bounds.width, bounds.height);
|
||||
let transformation = transformation * translate * scale;
|
||||
let matrix: [f32; 16] = transformation.into();
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&self.transform_location),
|
||||
false,
|
||||
&matrix,
|
||||
);
|
||||
|
||||
gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
gl.bind_texture(glow::TEXTURE_2D, None);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
gl.bind_vertex_array(None);
|
||||
gl.use_program(None);
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trim_cache(&mut self, mut gl: &glow::Context) {
|
||||
#[cfg(feature = "image")]
|
||||
self.raster_cache
|
||||
.borrow_mut()
|
||||
.trim(&mut self.storage, &mut gl);
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
self.vector_cache
|
||||
.borrow_mut()
|
||||
.trim(&mut self.storage, &mut gl);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
use iced_graphics::image;
|
||||
use iced_graphics::Size;
|
||||
|
||||
use glow::HasContext;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Storage;
|
||||
|
||||
impl image::Storage for Storage {
|
||||
type Entry = Entry;
|
||||
type State<'a> = &'a glow::Context;
|
||||
|
||||
fn upload(
|
||||
&mut self,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: &[u8],
|
||||
gl: &mut &glow::Context,
|
||||
) -> Option<Self::Entry> {
|
||||
unsafe {
|
||||
let texture = gl.create_texture().expect("create texture");
|
||||
gl.bind_texture(glow::TEXTURE_2D, Some(texture));
|
||||
gl.tex_image_2d(
|
||||
glow::TEXTURE_2D,
|
||||
0,
|
||||
glow::SRGB8_ALPHA8 as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
glow::RGBA,
|
||||
glow::UNSIGNED_BYTE,
|
||||
Some(data),
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_WRAP_S,
|
||||
glow::CLAMP_TO_EDGE as _,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_WRAP_T,
|
||||
glow::CLAMP_TO_EDGE as _,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_MIN_FILTER,
|
||||
glow::LINEAR as _,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_MAG_FILTER,
|
||||
glow::LINEAR as _,
|
||||
);
|
||||
gl.bind_texture(glow::TEXTURE_2D, None);
|
||||
|
||||
Some(Entry {
|
||||
size: Size::new(width, height),
|
||||
texture,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, entry: &Entry, gl: &mut &glow::Context) {
|
||||
unsafe { gl.delete_texture(entry.texture) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Entry {
|
||||
size: Size<u32>,
|
||||
pub(super) texture: glow::NativeTexture,
|
||||
}
|
||||
|
||||
impl image::storage::Entry for Entry {
|
||||
fn size(&self) -> Size<u32> {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
//! A [`glow`] renderer for [`iced_native`].
|
||||
//!
|
||||
//! 
|
||||
//!
|
||||
//! [`glow`]: https://github.com/grovesNL/glow
|
||||
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
|
||||
)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
unused_results,
|
||||
clippy::extra_unused_lifetimes,
|
||||
clippy::from_over_into,
|
||||
clippy::needless_borrow,
|
||||
clippy::new_without_default,
|
||||
clippy::useless_conversion
|
||||
)]
|
||||
#![forbid(rust_2018_idioms)]
|
||||
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub use glow;
|
||||
|
||||
mod backend;
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
mod image;
|
||||
mod program;
|
||||
mod quad;
|
||||
mod text;
|
||||
mod triangle;
|
||||
|
||||
pub mod settings;
|
||||
pub mod window;
|
||||
|
||||
pub use backend::Backend;
|
||||
pub use settings::Settings;
|
||||
|
||||
pub(crate) use iced_graphics::Transformation;
|
||||
|
||||
pub use iced_graphics::{Error, Viewport};
|
||||
pub use iced_native::Theme;
|
||||
|
||||
pub use iced_native::alignment;
|
||||
pub use iced_native::{Alignment, Background, Color, Command, Length, Vector};
|
||||
|
||||
/// A [`glow`] graphics renderer for [`iced`].
|
||||
///
|
||||
/// [`glow`]: https://github.com/grovesNL/glow
|
||||
/// [`iced`]: https://github.com/iced-rs/iced
|
||||
pub type Renderer<Theme = iced_native::Theme> =
|
||||
iced_graphics::Renderer<Backend, Theme>;
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
use glow::HasContext;
|
||||
|
||||
/// The [`Version`] of a `Program`.
|
||||
pub struct Version {
|
||||
vertex: String,
|
||||
fragment: String,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
pub fn new(gl: &glow::Context) -> Version {
|
||||
let version = gl.version();
|
||||
|
||||
let (vertex, fragment) = match (
|
||||
version.major,
|
||||
version.minor,
|
||||
version.is_embedded,
|
||||
) {
|
||||
// OpenGL 3.0+
|
||||
(3, 0 | 1 | 2, false) => (
|
||||
format!("#version 1{}0\n#extension GL_ARB_explicit_attrib_location : enable", version.minor + 3),
|
||||
format!(
|
||||
"#version 1{}0\n#extension GL_ARB_explicit_attrib_location : enable\n#define HIGHER_THAN_300 1",
|
||||
version.minor + 3
|
||||
),
|
||||
),
|
||||
// OpenGL 3.3+
|
||||
(3 | 4, _, false) => (
|
||||
format!("#version {}{}0\n#extension GL_ARB_explicit_attrib_location : enable", version.major, version.minor),
|
||||
format!(
|
||||
"#version {}{}0\n#extension GL_ARB_explicit_attrib_location : enable\n#define HIGHER_THAN_300 1",
|
||||
version.major, version.minor
|
||||
),
|
||||
),
|
||||
// OpenGL ES 3.0+
|
||||
(3, _, true) => (
|
||||
format!("#version 3{}0 es", version.minor),
|
||||
format!(
|
||||
"#version 3{}0 es\n#define HIGHER_THAN_300 1",
|
||||
version.minor
|
||||
),
|
||||
),
|
||||
// OpenGL ES 2.0+
|
||||
(2, _, true) => (
|
||||
String::from(
|
||||
"#version 100\n#define in attribute\n#define out varying",
|
||||
),
|
||||
String::from("#version 100\n#define in varying"),
|
||||
),
|
||||
// OpenGL 2.1
|
||||
(2, _, false) => (
|
||||
String::from(
|
||||
"#version 120\n#define in attribute\n#define out varying",
|
||||
),
|
||||
String::from("#version 120\n#define in varying"),
|
||||
),
|
||||
// OpenGL 1.1+
|
||||
_ => panic!("Incompatible context version: {version:?}"),
|
||||
};
|
||||
|
||||
log::info!("Shader directive: {}", vertex.lines().next().unwrap());
|
||||
|
||||
Version { vertex, fragment }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Shader(<glow::Context as HasContext>::Shader);
|
||||
|
||||
impl Shader {
|
||||
fn compile(gl: &glow::Context, stage: u32, content: &str) -> Shader {
|
||||
unsafe {
|
||||
let shader = gl.create_shader(stage).expect("Cannot create shader");
|
||||
|
||||
gl.shader_source(shader, content);
|
||||
gl.compile_shader(shader);
|
||||
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
panic!("{}", gl.get_shader_info_log(shader));
|
||||
}
|
||||
|
||||
Shader(shader)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a vertex [`Shader`].
|
||||
pub fn vertex(
|
||||
gl: &glow::Context,
|
||||
version: &Version,
|
||||
content: &'static str,
|
||||
) -> Self {
|
||||
let content = format!("{}\n{}", version.vertex, content);
|
||||
|
||||
Shader::compile(gl, glow::VERTEX_SHADER, &content)
|
||||
}
|
||||
|
||||
/// Creates a fragment [`Shader`].
|
||||
pub fn fragment(
|
||||
gl: &glow::Context,
|
||||
version: &Version,
|
||||
content: &'static str,
|
||||
) -> Self {
|
||||
let content = format!("{}\n{}", version.fragment, content);
|
||||
|
||||
Shader::compile(gl, glow::FRAGMENT_SHADER, &content)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn create(
|
||||
gl: &glow::Context,
|
||||
shaders: &[Shader],
|
||||
attributes: &[(u32, &str)],
|
||||
) -> <glow::Context as HasContext>::Program {
|
||||
let program = gl.create_program().expect("Cannot create program");
|
||||
|
||||
for shader in shaders {
|
||||
gl.attach_shader(program, shader.0);
|
||||
}
|
||||
|
||||
for (i, name) in attributes {
|
||||
gl.bind_attrib_location(program, *i, name);
|
||||
}
|
||||
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
panic!("{}", gl.get_program_info_log(program));
|
||||
}
|
||||
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader.0);
|
||||
gl.delete_shader(shader.0);
|
||||
}
|
||||
|
||||
program
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
mod compatibility;
|
||||
mod core;
|
||||
|
||||
use crate::program;
|
||||
use crate::Transformation;
|
||||
use glow::HasContext;
|
||||
use iced_graphics::layer;
|
||||
use iced_native::Rectangle;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
use tracing::info_span;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Pipeline {
|
||||
Core(core::Pipeline),
|
||||
Compatibility(compatibility::Pipeline),
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
gl: &glow::Context,
|
||||
shader_version: &program::Version,
|
||||
) -> Pipeline {
|
||||
let gl_version = gl.version();
|
||||
|
||||
// OpenGL 3.0+ and OpenGL ES 3.0+ have instancing (which is what separates `core` from `compatibility`)
|
||||
if gl_version.major >= 3 {
|
||||
log::info!("Mode: core");
|
||||
Pipeline::Core(core::Pipeline::new(gl, shader_version))
|
||||
} else {
|
||||
log::info!("Mode: compatibility");
|
||||
Pipeline::Compatibility(compatibility::Pipeline::new(
|
||||
gl,
|
||||
shader_version,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
target_height: u32,
|
||||
instances: &[layer::Quad],
|
||||
transformation: Transformation,
|
||||
scale: f32,
|
||||
bounds: Rectangle<u32>,
|
||||
) {
|
||||
#[cfg(feature = "tracing")]
|
||||
let _ = info_span!("Glow::Quad", "DRAW").enter();
|
||||
|
||||
match self {
|
||||
Pipeline::Core(pipeline) => {
|
||||
pipeline.draw(
|
||||
gl,
|
||||
target_height,
|
||||
instances,
|
||||
transformation,
|
||||
scale,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
Pipeline::Compatibility(pipeline) => {
|
||||
pipeline.draw(
|
||||
gl,
|
||||
target_height,
|
||||
instances,
|
||||
transformation,
|
||||
scale,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
use crate::program::{self, Shader};
|
||||
use crate::Transformation;
|
||||
use glow::HasContext;
|
||||
use iced_graphics::layer;
|
||||
use iced_native::Rectangle;
|
||||
|
||||
// Only change `MAX_QUADS`, otherwise you could cause problems
|
||||
// by splitting a triangle into different render passes.
|
||||
const MAX_QUADS: usize = 100_000;
|
||||
const MAX_VERTICES: usize = MAX_QUADS * 4;
|
||||
const MAX_INDICES: usize = MAX_QUADS * 6;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
program: <glow::Context as HasContext>::Program,
|
||||
vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||
vertex_buffer: <glow::Context as HasContext>::Buffer,
|
||||
index_buffer: <glow::Context as HasContext>::Buffer,
|
||||
transform_location: <glow::Context as HasContext>::UniformLocation,
|
||||
scale_location: <glow::Context as HasContext>::UniformLocation,
|
||||
screen_height_location: <glow::Context as HasContext>::UniformLocation,
|
||||
current_transform: Transformation,
|
||||
current_scale: f32,
|
||||
current_target_height: u32,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
gl: &glow::Context,
|
||||
shader_version: &program::Version,
|
||||
) -> Pipeline {
|
||||
let program = unsafe {
|
||||
let vertex_shader = Shader::vertex(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("../shader/compatibility/quad.vert"),
|
||||
);
|
||||
let fragment_shader = Shader::fragment(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("../shader/compatibility/quad.frag"),
|
||||
);
|
||||
|
||||
program::create(
|
||||
gl,
|
||||
&[vertex_shader, fragment_shader],
|
||||
&[
|
||||
(0, "i_Pos"),
|
||||
(1, "i_Scale"),
|
||||
(2, "i_Color"),
|
||||
(3, "i_BorderColor"),
|
||||
(4, "i_BorderRadius"),
|
||||
(5, "i_BorderWidth"),
|
||||
],
|
||||
)
|
||||
};
|
||||
|
||||
let transform_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||
.expect("Get transform location");
|
||||
|
||||
let scale_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Scale") }
|
||||
.expect("Get scale location");
|
||||
|
||||
let screen_height_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
|
||||
.expect("Get target height location");
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&transform_location),
|
||||
false,
|
||||
Transformation::identity().as_ref(),
|
||||
);
|
||||
|
||||
gl.uniform_1_f32(Some(&scale_location), 1.0);
|
||||
gl.uniform_1_f32(Some(&screen_height_location), 0.0);
|
||||
|
||||
gl.use_program(None);
|
||||
}
|
||||
|
||||
let (vertex_array, vertex_buffer, index_buffer) =
|
||||
unsafe { create_buffers(gl, MAX_VERTICES) };
|
||||
|
||||
Pipeline {
|
||||
program,
|
||||
vertex_array,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
transform_location,
|
||||
scale_location,
|
||||
screen_height_location,
|
||||
current_transform: Transformation::identity(),
|
||||
current_scale: 1.0,
|
||||
current_target_height: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
target_height: u32,
|
||||
instances: &[layer::Quad],
|
||||
transformation: Transformation,
|
||||
scale: f32,
|
||||
bounds: Rectangle<u32>,
|
||||
) {
|
||||
// TODO: Remove this allocation (probably by changing the shader and removing the need of two `position`)
|
||||
let vertices: Vec<Vertex> =
|
||||
instances.iter().flat_map(Vertex::from_quad).collect();
|
||||
|
||||
// TODO: Remove this allocation (or allocate only when needed)
|
||||
let indices: Vec<i32> = (0..instances.len().min(MAX_QUADS) as i32)
|
||||
.flat_map(|i| {
|
||||
[i * 4, 1 + i * 4, 2 + i * 4, 2 + i * 4, 1 + i * 4, 3 + i * 4]
|
||||
})
|
||||
.cycle()
|
||||
.take(instances.len() * 6)
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
gl.scissor(
|
||||
bounds.x as i32,
|
||||
(target_height - (bounds.y + bounds.height)) as i32,
|
||||
bounds.width as i32,
|
||||
bounds.height as i32,
|
||||
);
|
||||
|
||||
gl.use_program(Some(self.program));
|
||||
gl.bind_vertex_array(Some(self.vertex_array));
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
|
||||
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
|
||||
}
|
||||
|
||||
if transformation != self.current_transform {
|
||||
unsafe {
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&self.transform_location),
|
||||
false,
|
||||
transformation.as_ref(),
|
||||
);
|
||||
|
||||
self.current_transform = transformation;
|
||||
}
|
||||
}
|
||||
|
||||
if scale != self.current_scale {
|
||||
unsafe {
|
||||
gl.uniform_1_f32(Some(&self.scale_location), scale);
|
||||
}
|
||||
|
||||
self.current_scale = scale;
|
||||
}
|
||||
|
||||
if target_height != self.current_target_height {
|
||||
unsafe {
|
||||
gl.uniform_1_f32(
|
||||
Some(&self.screen_height_location),
|
||||
target_height as f32,
|
||||
);
|
||||
}
|
||||
|
||||
self.current_target_height = target_height;
|
||||
}
|
||||
|
||||
let passes = vertices
|
||||
.chunks(MAX_VERTICES)
|
||||
.zip(indices.chunks(MAX_INDICES));
|
||||
|
||||
for (vertices, indices) in passes {
|
||||
unsafe {
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
0,
|
||||
bytemuck::cast_slice(vertices),
|
||||
);
|
||||
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ELEMENT_ARRAY_BUFFER,
|
||||
0,
|
||||
bytemuck::cast_slice(indices),
|
||||
);
|
||||
|
||||
gl.draw_elements(
|
||||
glow::TRIANGLES,
|
||||
indices.len() as i32,
|
||||
glow::UNSIGNED_INT,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(None);
|
||||
gl.use_program(None);
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_buffers(
|
||||
gl: &glow::Context,
|
||||
size: usize,
|
||||
) -> (
|
||||
<glow::Context as HasContext>::VertexArray,
|
||||
<glow::Context as HasContext>::Buffer,
|
||||
<glow::Context as HasContext>::Buffer,
|
||||
) {
|
||||
let vertex_array = gl.create_vertex_array().expect("Create vertex array");
|
||||
let vertex_buffer = gl.create_buffer().expect("Create vertex buffer");
|
||||
let index_buffer = gl.create_buffer().expect("Create index buffer");
|
||||
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
|
||||
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
|
||||
gl.buffer_data_size(
|
||||
glow::ELEMENT_ARRAY_BUFFER,
|
||||
12 * size as i32,
|
||||
glow::DYNAMIC_DRAW,
|
||||
);
|
||||
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
|
||||
gl.buffer_data_size(
|
||||
glow::ARRAY_BUFFER,
|
||||
(size * Vertex::SIZE) as i32,
|
||||
glow::DYNAMIC_DRAW,
|
||||
);
|
||||
|
||||
let stride = Vertex::SIZE as i32;
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
|
||||
|
||||
gl.enable_vertex_attrib_array(1);
|
||||
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
|
||||
|
||||
gl.enable_vertex_attrib_array(2);
|
||||
gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
|
||||
|
||||
gl.enable_vertex_attrib_array(3);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
3,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4),
|
||||
);
|
||||
|
||||
gl.enable_vertex_attrib_array(4);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
4,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4),
|
||||
);
|
||||
|
||||
gl.enable_vertex_attrib_array(5);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
5,
|
||||
1,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4 + 4),
|
||||
);
|
||||
|
||||
gl.enable_vertex_attrib_array(6);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
6,
|
||||
2,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4 + 4 + 1),
|
||||
);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||
|
||||
(vertex_array, vertex_buffer, index_buffer)
|
||||
}
|
||||
|
||||
/// The vertex of a colored rectangle with a border.
|
||||
///
|
||||
/// This type can be directly uploaded to GPU memory.
|
||||
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex {
|
||||
/// The position of the [`Vertex`].
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// The size of the [`Vertex`].
|
||||
pub size: [f32; 2],
|
||||
|
||||
/// The color of the [`Vertex`], in __linear RGB__.
|
||||
pub color: [f32; 4],
|
||||
|
||||
/// The border color of the [`Vertex`], in __linear RGB__.
|
||||
pub border_color: [f32; 4],
|
||||
|
||||
/// The border radius of the [`Vertex`].
|
||||
pub border_radius: [f32; 4],
|
||||
|
||||
/// The border width of the [`Vertex`].
|
||||
pub border_width: f32,
|
||||
|
||||
/// The __quad__ position of the [`Vertex`].
|
||||
pub q_position: [f32; 2],
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
const SIZE: usize = std::mem::size_of::<Self>();
|
||||
|
||||
fn from_quad(quad: &layer::Quad) -> [Vertex; 4] {
|
||||
let base = Vertex {
|
||||
position: quad.position,
|
||||
size: quad.size,
|
||||
color: quad.color,
|
||||
border_color: quad.color,
|
||||
border_radius: quad.border_radius,
|
||||
border_width: quad.border_width,
|
||||
q_position: [0.0, 0.0],
|
||||
};
|
||||
|
||||
[
|
||||
base,
|
||||
Self {
|
||||
q_position: [0.0, 1.0],
|
||||
..base
|
||||
},
|
||||
Self {
|
||||
q_position: [1.0, 0.0],
|
||||
..base
|
||||
},
|
||||
Self {
|
||||
q_position: [1.0, 1.0],
|
||||
..base
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
use crate::program::{self, Shader};
|
||||
use crate::Transformation;
|
||||
use glow::HasContext;
|
||||
use iced_graphics::layer;
|
||||
use iced_native::Rectangle;
|
||||
|
||||
const MAX_INSTANCES: usize = 100_000;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
program: <glow::Context as HasContext>::Program,
|
||||
vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||
instances: <glow::Context as HasContext>::Buffer,
|
||||
transform_location: <glow::Context as HasContext>::UniformLocation,
|
||||
scale_location: <glow::Context as HasContext>::UniformLocation,
|
||||
screen_height_location: <glow::Context as HasContext>::UniformLocation,
|
||||
current_transform: Transformation,
|
||||
current_scale: f32,
|
||||
current_target_height: u32,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
gl: &glow::Context,
|
||||
shader_version: &program::Version,
|
||||
) -> Pipeline {
|
||||
let program = unsafe {
|
||||
let vertex_shader = Shader::vertex(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("../shader/core/quad.vert"),
|
||||
);
|
||||
let fragment_shader = Shader::fragment(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("../shader/core/quad.frag"),
|
||||
);
|
||||
|
||||
program::create(
|
||||
gl,
|
||||
&[vertex_shader, fragment_shader],
|
||||
&[
|
||||
(0, "i_Pos"),
|
||||
(1, "i_Scale"),
|
||||
(2, "i_Color"),
|
||||
(3, "i_BorderColor"),
|
||||
(4, "i_BorderRadius"),
|
||||
(5, "i_BorderWidth"),
|
||||
],
|
||||
)
|
||||
};
|
||||
|
||||
let transform_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||
.expect("Get transform location");
|
||||
|
||||
let scale_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Scale") }
|
||||
.expect("Get scale location");
|
||||
|
||||
let screen_height_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
|
||||
.expect("Get target height location");
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&transform_location),
|
||||
false,
|
||||
Transformation::identity().as_ref(),
|
||||
);
|
||||
|
||||
gl.uniform_1_f32(Some(&scale_location), 1.0);
|
||||
gl.uniform_1_f32(Some(&screen_height_location), 0.0);
|
||||
|
||||
gl.use_program(None);
|
||||
}
|
||||
|
||||
let (vertex_array, instances) =
|
||||
unsafe { create_instance_buffer(gl, MAX_INSTANCES) };
|
||||
|
||||
Pipeline {
|
||||
program,
|
||||
vertex_array,
|
||||
instances,
|
||||
transform_location,
|
||||
scale_location,
|
||||
screen_height_location,
|
||||
current_transform: Transformation::identity(),
|
||||
current_scale: 1.0,
|
||||
current_target_height: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
target_height: u32,
|
||||
instances: &[layer::Quad],
|
||||
transformation: Transformation,
|
||||
scale: f32,
|
||||
bounds: Rectangle<u32>,
|
||||
) {
|
||||
unsafe {
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
gl.scissor(
|
||||
bounds.x as i32,
|
||||
(target_height - (bounds.y + bounds.height)) as i32,
|
||||
bounds.width as i32,
|
||||
bounds.height as i32,
|
||||
);
|
||||
|
||||
gl.use_program(Some(self.program));
|
||||
gl.bind_vertex_array(Some(self.vertex_array));
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.instances));
|
||||
}
|
||||
|
||||
if transformation != self.current_transform {
|
||||
unsafe {
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&self.transform_location),
|
||||
false,
|
||||
transformation.as_ref(),
|
||||
);
|
||||
|
||||
self.current_transform = transformation;
|
||||
}
|
||||
}
|
||||
|
||||
if scale != self.current_scale {
|
||||
unsafe {
|
||||
gl.uniform_1_f32(Some(&self.scale_location), scale);
|
||||
}
|
||||
|
||||
self.current_scale = scale;
|
||||
}
|
||||
|
||||
if target_height != self.current_target_height {
|
||||
unsafe {
|
||||
gl.uniform_1_f32(
|
||||
Some(&self.screen_height_location),
|
||||
target_height as f32,
|
||||
);
|
||||
}
|
||||
|
||||
self.current_target_height = target_height;
|
||||
}
|
||||
|
||||
for instances in instances.chunks(MAX_INSTANCES) {
|
||||
unsafe {
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
0,
|
||||
bytemuck::cast_slice(instances),
|
||||
);
|
||||
|
||||
gl.draw_arrays_instanced(
|
||||
glow::TRIANGLE_STRIP,
|
||||
0,
|
||||
4,
|
||||
instances.len() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(None);
|
||||
gl.use_program(None);
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_instance_buffer(
|
||||
gl: &glow::Context,
|
||||
size: usize,
|
||||
) -> (
|
||||
<glow::Context as HasContext>::VertexArray,
|
||||
<glow::Context as HasContext>::Buffer,
|
||||
) {
|
||||
let vertex_array = gl.create_vertex_array().expect("Create vertex array");
|
||||
let buffer = gl.create_buffer().expect("Create instance buffer");
|
||||
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));
|
||||
gl.buffer_data_size(
|
||||
glow::ARRAY_BUFFER,
|
||||
(size * std::mem::size_of::<layer::Quad>()) as i32,
|
||||
glow::DYNAMIC_DRAW,
|
||||
);
|
||||
|
||||
let stride = std::mem::size_of::<layer::Quad>() as i32;
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
|
||||
gl.vertex_attrib_divisor(0, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(1);
|
||||
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
|
||||
gl.vertex_attrib_divisor(1, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(2);
|
||||
gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
|
||||
gl.vertex_attrib_divisor(2, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(3);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
3,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4),
|
||||
);
|
||||
gl.vertex_attrib_divisor(3, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(4);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
4,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4),
|
||||
);
|
||||
gl.vertex_attrib_divisor(4, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(5);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
5,
|
||||
1,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4 + 4),
|
||||
);
|
||||
gl.vertex_attrib_divisor(5, 1);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
|
||||
(vertex_array, buffer)
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
//! Configure a renderer.
|
||||
pub use iced_graphics::Antialiasing;
|
||||
|
||||
/// The settings of a [`Backend`].
|
||||
///
|
||||
/// [`Backend`]: crate::Backend
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Settings {
|
||||
/// The bytes of the font that will be used by default.
|
||||
///
|
||||
/// If `None` is provided, a default system font will be chosen.
|
||||
pub default_font: Option<&'static [u8]>,
|
||||
|
||||
/// The default size of text.
|
||||
///
|
||||
/// By default, it will be set to `20.0`.
|
||||
pub default_text_size: f32,
|
||||
|
||||
/// If enabled, spread text workload in multiple threads when multiple cores
|
||||
/// are available.
|
||||
///
|
||||
/// By default, it is disabled.
|
||||
pub text_multithreading: bool,
|
||||
|
||||
/// The antialiasing strategy that will be used for triangle primitives.
|
||||
///
|
||||
/// By default, it is `None`.
|
||||
pub antialiasing: Option<Antialiasing>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Settings {
|
||||
Settings {
|
||||
default_font: None,
|
||||
default_text_size: 20.0,
|
||||
text_multithreading: false,
|
||||
antialiasing: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Settings {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Settings")
|
||||
// Instead of printing the font bytes, we simply show a `bool` indicating if using a default font or not.
|
||||
.field("default_font", &self.default_font.is_some())
|
||||
.field("default_text_size", &self.default_text_size)
|
||||
.field("text_multithreading", &self.text_multithreading)
|
||||
.field("antialiasing", &self.antialiasing)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
/// Creates new [`Settings`] using environment configuration.
|
||||
///
|
||||
/// Currently, this is equivalent to calling [`Settings::default`].
|
||||
pub fn from_env() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HIGHER_THAN_300
|
||||
layout (location = 0) out vec4 fragColor;
|
||||
#define gl_FragColor fragColor
|
||||
#endif
|
||||
|
||||
in vec2 raw_position;
|
||||
|
||||
uniform vec4 gradient_direction;
|
||||
uniform int color_stops_size;
|
||||
// GLSL does not support dynamically sized arrays without SSBOs so this is capped to 16 stops
|
||||
//stored as color(vec4) -> offset(vec4) sequentially;
|
||||
uniform vec4 color_stops[32];
|
||||
|
||||
//TODO: rewrite without branching to make ALUs happy
|
||||
void main() {
|
||||
vec2 start = gradient_direction.xy;
|
||||
vec2 end = gradient_direction.zw;
|
||||
vec2 gradient_vec = vec2(end - start);
|
||||
vec2 current_vec = vec2(raw_position.xy - start);
|
||||
vec2 unit = normalize(gradient_vec);
|
||||
float coord_offset = dot(unit, current_vec) / length(gradient_vec);
|
||||
//if a gradient has a start/end stop that is identical, the mesh will have a transparent fill
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
float min_offset = color_stops[1].x;
|
||||
float max_offset = color_stops[color_stops_size - 1].x;
|
||||
|
||||
for (int i = 0; i < color_stops_size - 2; i += 2) {
|
||||
float curr_offset = color_stops[i+1].x;
|
||||
float next_offset = color_stops[i+3].x;
|
||||
|
||||
if (coord_offset <= min_offset) {
|
||||
//current coordinate is before the first defined offset, set it to the start color
|
||||
gl_FragColor = color_stops[0];
|
||||
}
|
||||
|
||||
if (curr_offset <= coord_offset && coord_offset <= next_offset) {
|
||||
//current fragment is between the current offset processing & the next one, interpolate colors
|
||||
gl_FragColor = mix(color_stops[i], color_stops[i+2], smoothstep(
|
||||
curr_offset,
|
||||
next_offset,
|
||||
coord_offset
|
||||
));
|
||||
}
|
||||
|
||||
if (coord_offset >= max_offset) {
|
||||
//current coordinate is before the last defined offset, set it to the last color
|
||||
gl_FragColor = color_stops[color_stops_size - 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
uniform mat4 u_Transform;
|
||||
|
||||
in vec2 i_Position;
|
||||
out vec2 raw_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
|
||||
raw_position = i_Position;
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
in vec2 tex_pos;
|
||||
|
||||
#ifdef HIGHER_THAN_300
|
||||
out vec4 fragColor;
|
||||
#define gl_FragColor fragColor
|
||||
#endif
|
||||
#ifdef GL_ES
|
||||
#define texture texture2D
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture(tex, tex_pos);
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
uniform mat4 u_Transform;
|
||||
|
||||
in vec2 i_Position;
|
||||
out vec2 tex_pos;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
|
||||
tex_pos = i_Position;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HIGHER_THAN_300
|
||||
out vec4 fragColor;
|
||||
#define gl_FragColor fragColor
|
||||
#endif
|
||||
|
||||
in vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = v_Color;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
uniform mat4 u_Transform;
|
||||
|
||||
in vec2 i_Position;
|
||||
in vec4 i_Color;
|
||||
|
||||
out vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
|
||||
v_Color = i_Color;
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uniform float u_ScreenHeight;
|
||||
|
||||
varying vec4 v_Color;
|
||||
varying vec4 v_BorderColor;
|
||||
varying vec2 v_Pos;
|
||||
varying vec2 v_Scale;
|
||||
varying vec4 v_BorderRadius;
|
||||
varying float v_BorderWidth;
|
||||
|
||||
float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius)
|
||||
{
|
||||
// TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
|
||||
vec2 inner_size = size - vec2(radius, radius) * 2.0;
|
||||
vec2 top_left = position + vec2(radius, radius);
|
||||
vec2 bottom_right = top_left + inner_size;
|
||||
|
||||
vec2 top_left_distance = top_left - frag_coord;
|
||||
vec2 bottom_right_distance = frag_coord - bottom_right;
|
||||
|
||||
vec2 distance = vec2(
|
||||
max(max(top_left_distance.x, bottom_right_distance.x), 0.0),
|
||||
max(max(top_left_distance.y, bottom_right_distance.y), 0.0)
|
||||
);
|
||||
|
||||
return sqrt(distance.x * distance.x + distance.y * distance.y);
|
||||
}
|
||||
|
||||
float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
|
||||
{
|
||||
float rx = radi.x;
|
||||
float ry = radi.y;
|
||||
rx = position.x > center.x ? radi.y : radi.x;
|
||||
ry = position.x > center.x ? radi.z : radi.w;
|
||||
rx = position.y > center.y ? ry : rx;
|
||||
return rx;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
|
||||
|
||||
float border_radius = selectBorderRadius(
|
||||
v_BorderRadius,
|
||||
fragCoord,
|
||||
(v_Pos + v_Scale * 0.5).xy
|
||||
);
|
||||
|
||||
float internal_border = max(border_radius - v_BorderWidth, 0.0);
|
||||
|
||||
float internal_distance = _distance(
|
||||
fragCoord,
|
||||
v_Pos + vec2(v_BorderWidth),
|
||||
v_Scale - vec2(v_BorderWidth * 2.0),
|
||||
internal_border
|
||||
);
|
||||
|
||||
float border_mix = smoothstep(
|
||||
max(internal_border - 0.5, 0.0),
|
||||
internal_border + 0.5,
|
||||
internal_distance
|
||||
);
|
||||
|
||||
vec4 mixed_color = mix(v_Color, v_BorderColor, border_mix);
|
||||
|
||||
float d = _distance(
|
||||
fragCoord,
|
||||
v_Pos,
|
||||
v_Scale,
|
||||
border_radius
|
||||
);
|
||||
|
||||
float radius_alpha =
|
||||
1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
|
||||
|
||||
gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
uniform mat4 u_Transform;
|
||||
uniform float u_Scale;
|
||||
|
||||
attribute vec2 i_Pos;
|
||||
attribute vec2 i_Scale;
|
||||
attribute vec4 i_Color;
|
||||
attribute vec4 i_BorderColor;
|
||||
attribute vec4 i_BorderRadius;
|
||||
attribute float i_BorderWidth;
|
||||
attribute vec2 q_Pos;
|
||||
|
||||
varying vec4 v_Color;
|
||||
varying vec4 v_BorderColor;
|
||||
varying vec2 v_Pos;
|
||||
varying vec2 v_Scale;
|
||||
varying vec4 v_BorderRadius;
|
||||
varying float v_BorderWidth;
|
||||
|
||||
|
||||
void main() {
|
||||
vec2 p_Pos = i_Pos * u_Scale;
|
||||
vec2 p_Scale = i_Scale * u_Scale;
|
||||
|
||||
vec4 i_BorderRadius = vec4(
|
||||
min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
|
||||
);
|
||||
|
||||
mat4 i_Transform = mat4(
|
||||
vec4(p_Scale.x + 1.0, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, p_Scale.y + 1.0, 0.0, 0.0),
|
||||
vec4(0.0, 0.0, 1.0, 0.0),
|
||||
vec4(p_Pos - vec2(0.5, 0.5), 0.0, 1.0)
|
||||
);
|
||||
|
||||
v_Color = i_Color;
|
||||
v_BorderColor = i_BorderColor;
|
||||
v_Pos = p_Pos;
|
||||
v_Scale = p_Scale;
|
||||
v_BorderRadius = i_BorderRadius * u_Scale;
|
||||
v_BorderWidth = i_BorderWidth * u_Scale;
|
||||
|
||||
gl_Position = u_Transform * i_Transform * vec4(q_Pos, 0.0, 1.0);
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HIGHER_THAN_300
|
||||
out vec4 fragColor;
|
||||
#define gl_FragColor fragColor
|
||||
#endif
|
||||
|
||||
uniform float u_ScreenHeight;
|
||||
|
||||
in vec4 v_Color;
|
||||
in vec4 v_BorderColor;
|
||||
in vec2 v_Pos;
|
||||
in vec2 v_Scale;
|
||||
in vec4 v_BorderRadius;
|
||||
in float v_BorderWidth;
|
||||
|
||||
float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius)
|
||||
{
|
||||
// TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
|
||||
vec2 inner_size = size - vec2(radius, radius) * 2.0;
|
||||
vec2 top_left = position + vec2(radius, radius);
|
||||
vec2 bottom_right = top_left + inner_size;
|
||||
|
||||
vec2 top_left_distance = top_left - frag_coord;
|
||||
vec2 bottom_right_distance = frag_coord - bottom_right;
|
||||
|
||||
vec2 distance = vec2(
|
||||
max(max(top_left_distance.x, bottom_right_distance.x), 0.0),
|
||||
max(max(top_left_distance.y, bottom_right_distance.y), 0.0)
|
||||
);
|
||||
|
||||
return sqrt(distance.x * distance.x + distance.y * distance.y);
|
||||
}
|
||||
|
||||
float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
|
||||
{
|
||||
float rx = radi.x;
|
||||
float ry = radi.y;
|
||||
rx = position.x > center.x ? radi.y : radi.x;
|
||||
ry = position.x > center.x ? radi.z : radi.w;
|
||||
rx = position.y > center.y ? ry : rx;
|
||||
return rx;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 mixed_color;
|
||||
|
||||
vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
|
||||
|
||||
float border_radius = selectBorderRadius(
|
||||
v_BorderRadius,
|
||||
fragCoord,
|
||||
(v_Pos + v_Scale * 0.5).xy
|
||||
);
|
||||
|
||||
// TODO: Remove branching (?)
|
||||
if(v_BorderWidth > 0.0) {
|
||||
float internal_border = max(border_radius - v_BorderWidth, 0.0);
|
||||
|
||||
float internal_distance = fDistance(
|
||||
fragCoord,
|
||||
v_Pos + vec2(v_BorderWidth),
|
||||
v_Scale - vec2(v_BorderWidth * 2.0),
|
||||
internal_border
|
||||
);
|
||||
|
||||
float border_mix = smoothstep(
|
||||
max(internal_border - 0.5, 0.0),
|
||||
internal_border + 0.5,
|
||||
internal_distance
|
||||
);
|
||||
|
||||
mixed_color = mix(v_Color, v_BorderColor, border_mix);
|
||||
} else {
|
||||
mixed_color = v_Color;
|
||||
}
|
||||
|
||||
float d = fDistance(
|
||||
fragCoord,
|
||||
v_Pos,
|
||||
v_Scale,
|
||||
border_radius
|
||||
);
|
||||
|
||||
float radius_alpha =
|
||||
1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
|
||||
|
||||
gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
uniform mat4 u_Transform;
|
||||
uniform float u_Scale;
|
||||
|
||||
in vec2 i_Pos;
|
||||
in vec2 i_Scale;
|
||||
in vec4 i_Color;
|
||||
in vec4 i_BorderColor;
|
||||
in vec4 i_BorderRadius;
|
||||
in float i_BorderWidth;
|
||||
|
||||
out vec4 v_Color;
|
||||
out vec4 v_BorderColor;
|
||||
out vec2 v_Pos;
|
||||
out vec2 v_Scale;
|
||||
out vec4 v_BorderRadius;
|
||||
out float v_BorderWidth;
|
||||
|
||||
vec2 positions[4] = vec2[](
|
||||
vec2(0.0, 0.0),
|
||||
vec2(0.0, 1.0),
|
||||
vec2(1.0, 0.0),
|
||||
vec2(1.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
vec2 q_Pos = positions[gl_VertexID];
|
||||
vec2 p_Pos = i_Pos * u_Scale;
|
||||
vec2 p_Scale = i_Scale * u_Scale;
|
||||
|
||||
vec4 i_BorderRadius = vec4(
|
||||
min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
|
||||
min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
|
||||
);
|
||||
|
||||
mat4 i_Transform = mat4(
|
||||
vec4(p_Scale.x + 1.0, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, p_Scale.y + 1.0, 0.0, 0.0),
|
||||
vec4(0.0, 0.0, 1.0, 0.0),
|
||||
vec4(p_Pos - vec2(0.5, 0.5), 0.0, 1.0)
|
||||
);
|
||||
|
||||
v_Color = i_Color;
|
||||
v_BorderColor = i_BorderColor;
|
||||
v_Pos = p_Pos;
|
||||
v_Scale = p_Scale;
|
||||
v_BorderRadius = i_BorderRadius * u_Scale;
|
||||
v_BorderWidth = i_BorderWidth * u_Scale;
|
||||
|
||||
gl_Position = u_Transform * i_Transform * vec4(q_Pos, 0.0, 1.0);
|
||||
}
|
||||
257
glow/src/text.rs
257
glow/src/text.rs
|
|
@ -1,257 +0,0 @@
|
|||
use crate::Transformation;
|
||||
|
||||
use iced_graphics::font;
|
||||
|
||||
use glow_glyph::ab_glyph;
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
|
||||
pub use iced_native::text::Hit;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
draw_brush: RefCell<glow_glyph::GlyphBrush>,
|
||||
draw_font_map: RefCell<HashMap<String, glow_glyph::FontId>>,
|
||||
measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
gl: &glow::Context,
|
||||
default_font: Option<&[u8]>,
|
||||
multithreading: bool,
|
||||
) -> Self {
|
||||
let default_font = default_font.map(|slice| slice.to_vec());
|
||||
|
||||
// TODO: Font customization
|
||||
#[cfg(feature = "default_system_font")]
|
||||
let default_font = {
|
||||
default_font.or_else(|| {
|
||||
font::Source::new()
|
||||
.load(&[font::Family::SansSerif, font::Family::Serif])
|
||||
.ok()
|
||||
})
|
||||
};
|
||||
|
||||
let default_font =
|
||||
default_font.unwrap_or_else(|| font::FALLBACK.to_vec());
|
||||
|
||||
let font = ab_glyph::FontArc::try_from_vec(default_font)
|
||||
.unwrap_or_else(|_| {
|
||||
log::warn!(
|
||||
"System font failed to load. Falling back to \
|
||||
embedded font..."
|
||||
);
|
||||
|
||||
ab_glyph::FontArc::try_from_slice(font::FALLBACK)
|
||||
.expect("Load fallback font")
|
||||
});
|
||||
|
||||
let draw_brush_builder =
|
||||
glow_glyph::GlyphBrushBuilder::using_font(font.clone())
|
||||
.initial_cache_size((2048, 2048))
|
||||
.draw_cache_multithread(multithreading);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
|
||||
|
||||
let draw_brush = draw_brush_builder.build(gl);
|
||||
|
||||
let measure_brush =
|
||||
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
||||
|
||||
Pipeline {
|
||||
draw_brush: RefCell::new(draw_brush),
|
||||
draw_font_map: RefCell::new(HashMap::new()),
|
||||
measure_brush: RefCell::new(measure_brush),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue(&mut self, section: glow_glyph::Section<'_>) {
|
||||
self.draw_brush.borrow_mut().queue(section);
|
||||
}
|
||||
|
||||
pub fn draw_queued(
|
||||
&mut self,
|
||||
gl: &glow::Context,
|
||||
transformation: Transformation,
|
||||
region: glow_glyph::Region,
|
||||
) {
|
||||
self.draw_brush
|
||||
.borrow_mut()
|
||||
.draw_queued_with_transform_and_scissoring(
|
||||
gl,
|
||||
transformation.into(),
|
||||
region,
|
||||
)
|
||||
.expect("Draw text");
|
||||
}
|
||||
|
||||
pub fn measure(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
font: iced_native::Font,
|
||||
bounds: iced_native::Size,
|
||||
) -> (f32, f32) {
|
||||
use glow_glyph::GlyphCruncher;
|
||||
|
||||
let glow_glyph::FontId(font_id) = self.find_font(font);
|
||||
|
||||
let section = glow_glyph::Section {
|
||||
bounds: (bounds.width, bounds.height),
|
||||
text: vec![glow_glyph::Text {
|
||||
text: content,
|
||||
scale: size.into(),
|
||||
font_id: glow_glyph::FontId(font_id),
|
||||
extra: glow_glyph::Extra::default(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(bounds) =
|
||||
self.measure_brush.borrow_mut().glyph_bounds(section)
|
||||
{
|
||||
(bounds.width().ceil(), bounds.height().ceil())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hit_test(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
font: iced_native::Font,
|
||||
bounds: iced_native::Size,
|
||||
point: iced_native::Point,
|
||||
nearest_only: bool,
|
||||
) -> Option<Hit> {
|
||||
use glow_glyph::GlyphCruncher;
|
||||
|
||||
let glow_glyph::FontId(font_id) = self.find_font(font);
|
||||
|
||||
let section = glow_glyph::Section {
|
||||
bounds: (bounds.width, bounds.height),
|
||||
text: vec![glow_glyph::Text {
|
||||
text: content,
|
||||
scale: size.into(),
|
||||
font_id: glow_glyph::FontId(font_id),
|
||||
extra: glow_glyph::Extra::default(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut mb = self.measure_brush.borrow_mut();
|
||||
|
||||
// The underlying type is FontArc, so clones are cheap.
|
||||
use ab_glyph::{Font, ScaleFont};
|
||||
let font = mb.fonts()[font_id].clone().into_scaled(size);
|
||||
|
||||
// Implements an iterator over the glyph bounding boxes.
|
||||
let bounds = mb.glyphs(section).map(
|
||||
|glow_glyph::SectionGlyph {
|
||||
byte_index, glyph, ..
|
||||
}| {
|
||||
(
|
||||
*byte_index,
|
||||
iced_native::Rectangle::new(
|
||||
iced_native::Point::new(
|
||||
glyph.position.x - font.h_side_bearing(glyph.id),
|
||||
glyph.position.y - font.ascent(),
|
||||
),
|
||||
iced_native::Size::new(
|
||||
font.h_advance(glyph.id),
|
||||
font.ascent() - font.descent(),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Implements computation of the character index based on the byte index
|
||||
// within the input string.
|
||||
let char_index = |byte_index| {
|
||||
let mut b_count = 0;
|
||||
for (i, utf8_len) in
|
||||
content.chars().map(|c| c.len_utf8()).enumerate()
|
||||
{
|
||||
if byte_index < (b_count + utf8_len) {
|
||||
return i;
|
||||
}
|
||||
b_count += utf8_len;
|
||||
}
|
||||
|
||||
byte_index
|
||||
};
|
||||
|
||||
if !nearest_only {
|
||||
for (idx, bounds) in bounds.clone() {
|
||||
if bounds.contains(point) {
|
||||
return Some(Hit::CharOffset(char_index(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nearest = bounds
|
||||
.map(|(index, bounds)| (index, bounds.center()))
|
||||
.min_by(|(_, center_a), (_, center_b)| {
|
||||
center_a
|
||||
.distance(point)
|
||||
.partial_cmp(¢er_b.distance(point))
|
||||
.unwrap_or(std::cmp::Ordering::Greater)
|
||||
});
|
||||
|
||||
nearest.map(|(idx, center)| {
|
||||
Hit::NearestCharOffset(char_index(idx), point - center)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn trim_measurement_cache(&mut self) {
|
||||
// TODO: We should probably use a `GlyphCalculator` for this. However,
|
||||
// it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
|
||||
// This makes stuff quite inconvenient. A manual method for trimming the
|
||||
// cache would make our lives easier.
|
||||
loop {
|
||||
let action = self
|
||||
.measure_brush
|
||||
.borrow_mut()
|
||||
.process_queued(|_, _| {}, |_| {});
|
||||
|
||||
match action {
|
||||
Ok(_) => break,
|
||||
Err(glyph_brush::BrushError::TextureTooSmall { suggested }) => {
|
||||
let (width, height) = suggested;
|
||||
|
||||
self.measure_brush
|
||||
.borrow_mut()
|
||||
.resize_texture(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_font(&self, font: iced_native::Font) -> glow_glyph::FontId {
|
||||
match font {
|
||||
iced_native::Font::Default => glow_glyph::FontId(0),
|
||||
iced_native::Font::External { name, bytes } => {
|
||||
if let Some(font_id) = self.draw_font_map.borrow().get(name) {
|
||||
return *font_id;
|
||||
}
|
||||
|
||||
let font = ab_glyph::FontArc::try_from_slice(bytes)
|
||||
.expect("Load font");
|
||||
|
||||
let _ = self.measure_brush.borrow_mut().add_font(font.clone());
|
||||
|
||||
let font_id = self.draw_brush.borrow_mut().add_font(font);
|
||||
|
||||
let _ = self
|
||||
.draw_font_map
|
||||
.borrow_mut()
|
||||
.insert(String::from(name), font_id);
|
||||
|
||||
font_id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,595 +0,0 @@
|
|||
//! Draw meshes of triangles.
|
||||
use crate::program;
|
||||
use crate::Transformation;
|
||||
|
||||
use iced_graphics::gradient::Gradient;
|
||||
use iced_graphics::layer::mesh::{self, Mesh};
|
||||
use iced_graphics::triangle::{ColoredVertex2D, Vertex2D};
|
||||
|
||||
use glow::HasContext;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
use tracing::info_span;
|
||||
|
||||
const DEFAULT_VERTICES: usize = 1_000;
|
||||
const DEFAULT_INDICES: usize = 1_000;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pipeline {
|
||||
indices: Buffer<u32>,
|
||||
solid: solid::Program,
|
||||
gradient: gradient::Program,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(gl: &glow::Context, shader_version: &program::Version) -> Self {
|
||||
let mut indices = unsafe {
|
||||
Buffer::new(
|
||||
gl,
|
||||
glow::ELEMENT_ARRAY_BUFFER,
|
||||
glow::DYNAMIC_DRAW,
|
||||
DEFAULT_INDICES,
|
||||
)
|
||||
};
|
||||
|
||||
let solid = solid::Program::new(gl, shader_version);
|
||||
let gradient = gradient::Program::new(gl, shader_version);
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(Some(solid.vertex_array));
|
||||
indices.bind(gl, 0);
|
||||
|
||||
gl.bind_vertex_array(Some(gradient.vertex_array));
|
||||
indices.bind(gl, 0);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
}
|
||||
|
||||
Self {
|
||||
indices,
|
||||
solid,
|
||||
gradient,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
meshes: &[Mesh<'_>],
|
||||
gl: &glow::Context,
|
||||
target_height: u32,
|
||||
transformation: Transformation,
|
||||
scale_factor: f32,
|
||||
) {
|
||||
#[cfg(feature = "tracing")]
|
||||
let _ = info_span!("Glow::Triangle", "DRAW").enter();
|
||||
|
||||
unsafe {
|
||||
gl.enable(glow::MULTISAMPLE);
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
}
|
||||
|
||||
// Count the total amount of vertices & indices we need to handle
|
||||
let count = mesh::attribute_count_of(meshes);
|
||||
|
||||
// Then we ensure the current attribute buffers are big enough, resizing if necessary
|
||||
unsafe {
|
||||
self.indices.bind(gl, count.indices);
|
||||
}
|
||||
|
||||
// We upload all the vertices and indices upfront
|
||||
let mut solid_vertex_offset = 0;
|
||||
let mut gradient_vertex_offset = 0;
|
||||
let mut index_offset = 0;
|
||||
|
||||
for mesh in meshes {
|
||||
let indices = mesh.indices();
|
||||
|
||||
unsafe {
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ELEMENT_ARRAY_BUFFER,
|
||||
(index_offset * std::mem::size_of::<u32>()) as i32,
|
||||
bytemuck::cast_slice(indices),
|
||||
);
|
||||
|
||||
index_offset += indices.len();
|
||||
}
|
||||
|
||||
match mesh {
|
||||
Mesh::Solid { buffers, .. } => {
|
||||
unsafe {
|
||||
self.solid.vertices.bind(gl, count.solid_vertices);
|
||||
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
(solid_vertex_offset
|
||||
* std::mem::size_of::<ColoredVertex2D>())
|
||||
as i32,
|
||||
bytemuck::cast_slice(&buffers.vertices),
|
||||
);
|
||||
}
|
||||
|
||||
solid_vertex_offset += buffers.vertices.len();
|
||||
}
|
||||
Mesh::Gradient { buffers, .. } => {
|
||||
unsafe {
|
||||
self.gradient
|
||||
.vertices
|
||||
.bind(gl, count.gradient_vertices);
|
||||
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
(gradient_vertex_offset
|
||||
* std::mem::size_of::<Vertex2D>())
|
||||
as i32,
|
||||
bytemuck::cast_slice(&buffers.vertices),
|
||||
);
|
||||
}
|
||||
|
||||
gradient_vertex_offset += buffers.vertices.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then we draw each mesh using offsets
|
||||
let mut last_solid_vertex = 0;
|
||||
let mut last_gradient_vertex = 0;
|
||||
let mut last_index = 0;
|
||||
|
||||
for mesh in meshes {
|
||||
let indices = mesh.indices();
|
||||
let origin = mesh.origin();
|
||||
|
||||
let transform =
|
||||
transformation * Transformation::translate(origin.x, origin.y);
|
||||
|
||||
let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
|
||||
|
||||
unsafe {
|
||||
gl.scissor(
|
||||
clip_bounds.x as i32,
|
||||
(target_height - (clip_bounds.y + clip_bounds.height))
|
||||
as i32,
|
||||
clip_bounds.width as i32,
|
||||
clip_bounds.height as i32,
|
||||
);
|
||||
}
|
||||
|
||||
match mesh {
|
||||
Mesh::Solid { buffers, .. } => unsafe {
|
||||
gl.use_program(Some(self.solid.program));
|
||||
gl.bind_vertex_array(Some(self.solid.vertex_array));
|
||||
|
||||
if transform != self.solid.uniforms.transform {
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&self.solid.uniforms.transform_location),
|
||||
false,
|
||||
transform.as_ref(),
|
||||
);
|
||||
|
||||
self.solid.uniforms.transform = transform;
|
||||
}
|
||||
|
||||
gl.draw_elements_base_vertex(
|
||||
glow::TRIANGLES,
|
||||
indices.len() as i32,
|
||||
glow::UNSIGNED_INT,
|
||||
(last_index * std::mem::size_of::<u32>()) as i32,
|
||||
last_solid_vertex as i32,
|
||||
);
|
||||
|
||||
last_solid_vertex += buffers.vertices.len();
|
||||
},
|
||||
Mesh::Gradient {
|
||||
buffers, gradient, ..
|
||||
} => unsafe {
|
||||
gl.use_program(Some(self.gradient.program));
|
||||
gl.bind_vertex_array(Some(self.gradient.vertex_array));
|
||||
|
||||
if transform != self.gradient.uniforms.transform {
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&self.gradient.uniforms.locations.transform),
|
||||
false,
|
||||
transform.as_ref(),
|
||||
);
|
||||
|
||||
self.gradient.uniforms.transform = transform;
|
||||
}
|
||||
|
||||
if &self.gradient.uniforms.gradient != *gradient {
|
||||
match gradient {
|
||||
Gradient::Linear(linear) => {
|
||||
gl.uniform_4_f32(
|
||||
Some(
|
||||
&self
|
||||
.gradient
|
||||
.uniforms
|
||||
.locations
|
||||
.gradient_direction,
|
||||
),
|
||||
linear.start.x,
|
||||
linear.start.y,
|
||||
linear.end.x,
|
||||
linear.end.y,
|
||||
);
|
||||
|
||||
gl.uniform_1_i32(
|
||||
Some(
|
||||
&self
|
||||
.gradient
|
||||
.uniforms
|
||||
.locations
|
||||
.color_stops_size,
|
||||
),
|
||||
(linear.color_stops.len() * 2) as i32,
|
||||
);
|
||||
|
||||
let mut stops = [0.0; 128];
|
||||
|
||||
for (index, stop) in linear
|
||||
.color_stops
|
||||
.iter()
|
||||
.enumerate()
|
||||
.take(16)
|
||||
{
|
||||
let [r, g, b, a] = stop.color.into_linear();
|
||||
|
||||
stops[index * 8] = r;
|
||||
stops[(index * 8) + 1] = g;
|
||||
stops[(index * 8) + 2] = b;
|
||||
stops[(index * 8) + 3] = a;
|
||||
stops[(index * 8) + 4] = stop.offset;
|
||||
stops[(index * 8) + 5] = 0.;
|
||||
stops[(index * 8) + 6] = 0.;
|
||||
stops[(index * 8) + 7] = 0.;
|
||||
}
|
||||
|
||||
gl.uniform_4_f32_slice(
|
||||
Some(
|
||||
&self
|
||||
.gradient
|
||||
.uniforms
|
||||
.locations
|
||||
.color_stops,
|
||||
),
|
||||
&stops,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.gradient.uniforms.gradient = (*gradient).clone();
|
||||
}
|
||||
|
||||
gl.draw_elements_base_vertex(
|
||||
glow::TRIANGLES,
|
||||
indices.len() as i32,
|
||||
glow::UNSIGNED_INT,
|
||||
(last_index * std::mem::size_of::<u32>()) as i32,
|
||||
last_gradient_vertex as i32,
|
||||
);
|
||||
|
||||
last_gradient_vertex += buffers.vertices.len();
|
||||
},
|
||||
}
|
||||
|
||||
last_index += indices.len();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(None);
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
gl.disable(glow::MULTISAMPLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Buffer<T> {
|
||||
raw: <glow::Context as HasContext>::Buffer,
|
||||
target: u32,
|
||||
usage: u32,
|
||||
size: usize,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Buffer<T> {
|
||||
pub unsafe fn new(
|
||||
gl: &glow::Context,
|
||||
target: u32,
|
||||
usage: u32,
|
||||
size: usize,
|
||||
) -> Self {
|
||||
let raw = gl.create_buffer().expect("Create buffer");
|
||||
|
||||
let mut buffer = Buffer {
|
||||
raw,
|
||||
target,
|
||||
usage,
|
||||
size: 0,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
buffer.bind(gl, size);
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
pub unsafe fn bind(&mut self, gl: &glow::Context, size: usize) {
|
||||
gl.bind_buffer(self.target, Some(self.raw));
|
||||
|
||||
if self.size < size {
|
||||
gl.buffer_data_size(
|
||||
self.target,
|
||||
(size * std::mem::size_of::<T>()) as i32,
|
||||
self.usage,
|
||||
);
|
||||
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod solid {
|
||||
use crate::program;
|
||||
use crate::triangle;
|
||||
use glow::{Context, HasContext, NativeProgram};
|
||||
use iced_graphics::triangle::ColoredVertex2D;
|
||||
use iced_graphics::Transformation;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Program {
|
||||
pub program: <Context as HasContext>::Program,
|
||||
pub vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||
pub vertices: triangle::Buffer<ColoredVertex2D>,
|
||||
pub uniforms: Uniforms,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(gl: &Context, shader_version: &program::Version) -> Self {
|
||||
let program = unsafe {
|
||||
let vertex_shader = program::Shader::vertex(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/solid.vert"),
|
||||
);
|
||||
|
||||
let fragment_shader = program::Shader::fragment(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/solid.frag"),
|
||||
);
|
||||
|
||||
program::create(
|
||||
gl,
|
||||
&[vertex_shader, fragment_shader],
|
||||
&[(0, "i_Position"), (1, "i_Color")],
|
||||
)
|
||||
};
|
||||
|
||||
let vertex_array = unsafe {
|
||||
gl.create_vertex_array().expect("Create vertex array")
|
||||
};
|
||||
|
||||
let vertices = unsafe {
|
||||
triangle::Buffer::new(
|
||||
gl,
|
||||
glow::ARRAY_BUFFER,
|
||||
glow::DYNAMIC_DRAW,
|
||||
super::DEFAULT_VERTICES,
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
|
||||
let stride = std::mem::size_of::<ColoredVertex2D>() as i32;
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
0,
|
||||
2,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
0,
|
||||
);
|
||||
|
||||
gl.enable_vertex_attrib_array(1);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
1,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * 2,
|
||||
);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
};
|
||||
|
||||
Self {
|
||||
program,
|
||||
vertex_array,
|
||||
vertices,
|
||||
uniforms: Uniforms::new(gl, program),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Uniforms {
|
||||
pub transform: Transformation,
|
||||
pub transform_location: <Context as HasContext>::UniformLocation,
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
fn new(gl: &Context, program: NativeProgram) -> Self {
|
||||
let transform = Transformation::identity();
|
||||
let transform_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||
.expect("Solid - Get u_Transform.");
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&transform_location),
|
||||
false,
|
||||
transform.as_ref(),
|
||||
);
|
||||
|
||||
gl.use_program(None);
|
||||
}
|
||||
|
||||
Self {
|
||||
transform,
|
||||
transform_location,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod gradient {
|
||||
use crate::program;
|
||||
use crate::triangle;
|
||||
use glow::{Context, HasContext, NativeProgram};
|
||||
use iced_graphics::gradient::{self, Gradient};
|
||||
use iced_graphics::triangle::Vertex2D;
|
||||
use iced_graphics::Transformation;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Program {
|
||||
pub program: <Context as HasContext>::Program,
|
||||
pub vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||
pub vertices: triangle::Buffer<Vertex2D>,
|
||||
pub uniforms: Uniforms,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(gl: &Context, shader_version: &program::Version) -> Self {
|
||||
let program = unsafe {
|
||||
let vertex_shader = program::Shader::vertex(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/gradient.vert"),
|
||||
);
|
||||
|
||||
let fragment_shader = program::Shader::fragment(
|
||||
gl,
|
||||
shader_version,
|
||||
include_str!("shader/common/gradient.frag"),
|
||||
);
|
||||
|
||||
program::create(
|
||||
gl,
|
||||
&[vertex_shader, fragment_shader],
|
||||
&[(0, "i_Position")],
|
||||
)
|
||||
};
|
||||
|
||||
let vertex_array = unsafe {
|
||||
gl.create_vertex_array().expect("Create vertex array")
|
||||
};
|
||||
|
||||
let vertices = unsafe {
|
||||
triangle::Buffer::new(
|
||||
gl,
|
||||
glow::ARRAY_BUFFER,
|
||||
glow::DYNAMIC_DRAW,
|
||||
super::DEFAULT_VERTICES,
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gl.bind_vertex_array(Some(vertex_array));
|
||||
|
||||
let stride = std::mem::size_of::<Vertex2D>() as i32;
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
0,
|
||||
2,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
0,
|
||||
);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
};
|
||||
|
||||
Self {
|
||||
program,
|
||||
vertex_array,
|
||||
vertices,
|
||||
uniforms: Uniforms::new(gl, program),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Uniforms {
|
||||
pub gradient: Gradient,
|
||||
pub transform: Transformation,
|
||||
pub locations: Locations,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Locations {
|
||||
pub gradient_direction: <Context as HasContext>::UniformLocation,
|
||||
pub color_stops_size: <Context as HasContext>::UniformLocation,
|
||||
//currently the maximum number of stops is 16 due to lack of SSBO in GL2.1
|
||||
pub color_stops: <Context as HasContext>::UniformLocation,
|
||||
pub transform: <Context as HasContext>::UniformLocation,
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
fn new(gl: &Context, program: NativeProgram) -> Self {
|
||||
let gradient_direction = unsafe {
|
||||
gl.get_uniform_location(program, "gradient_direction")
|
||||
}
|
||||
.expect("Gradient - Get gradient_direction.");
|
||||
|
||||
let color_stops_size =
|
||||
unsafe { gl.get_uniform_location(program, "color_stops_size") }
|
||||
.expect("Gradient - Get color_stops_size.");
|
||||
|
||||
let color_stops = unsafe {
|
||||
gl.get_uniform_location(program, "color_stops")
|
||||
.expect("Gradient - Get color_stops.")
|
||||
};
|
||||
|
||||
let transform = Transformation::identity();
|
||||
let transform_location =
|
||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||
.expect("Solid - Get u_Transform.");
|
||||
|
||||
unsafe {
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.uniform_matrix_4_f32_slice(
|
||||
Some(&transform_location),
|
||||
false,
|
||||
transform.as_ref(),
|
||||
);
|
||||
|
||||
gl.use_program(None);
|
||||
}
|
||||
|
||||
Self {
|
||||
gradient: Gradient::Linear(gradient::Linear {
|
||||
start: Default::default(),
|
||||
end: Default::default(),
|
||||
color_stops: vec![],
|
||||
}),
|
||||
transform: Transformation::identity(),
|
||||
locations: Locations {
|
||||
gradient_direction,
|
||||
color_stops_size,
|
||||
color_stops,
|
||||
transform: transform_location,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
//! Display rendering results on windows.
|
||||
mod compositor;
|
||||
|
||||
pub use compositor::Compositor;
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
use crate::{Backend, Color, Error, Renderer, Settings, Viewport};
|
||||
|
||||
use glow::HasContext;
|
||||
use iced_graphics::{compositor, Antialiasing, Size};
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A window graphics backend for iced powered by `glow`.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Compositor<Theme> {
|
||||
gl: glow::Context,
|
||||
theme: PhantomData<Theme>,
|
||||
}
|
||||
|
||||
impl<Theme> iced_graphics::window::GLCompositor for Compositor<Theme> {
|
||||
type Settings = Settings;
|
||||
type Renderer = Renderer<Theme>;
|
||||
|
||||
unsafe fn new(
|
||||
settings: Self::Settings,
|
||||
loader_function: impl FnMut(&str) -> *const c_void,
|
||||
) -> Result<(Self, Self::Renderer), Error> {
|
||||
let gl = glow::Context::from_loader_function(loader_function);
|
||||
|
||||
log::info!("{:#?}", settings);
|
||||
|
||||
let version = gl.version();
|
||||
log::info!(
|
||||
"OpenGL version: {:?} (Embedded: {})",
|
||||
version,
|
||||
version.is_embedded
|
||||
);
|
||||
|
||||
let renderer = gl.get_parameter_string(glow::RENDERER);
|
||||
log::info!("Renderer: {}", renderer);
|
||||
|
||||
// Enable auto-conversion from/to sRGB
|
||||
gl.enable(glow::FRAMEBUFFER_SRGB);
|
||||
|
||||
// Enable alpha blending
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func_separate(
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
glow::ONE,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
|
||||
// Disable multisampling by default
|
||||
gl.disable(glow::MULTISAMPLE);
|
||||
|
||||
let renderer = Renderer::new(Backend::new(&gl, settings));
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
gl,
|
||||
theme: PhantomData,
|
||||
},
|
||||
renderer,
|
||||
))
|
||||
}
|
||||
|
||||
fn sample_count(settings: &Settings) -> u32 {
|
||||
settings
|
||||
.antialiasing
|
||||
.map(Antialiasing::sample_count)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn resize_viewport(&mut self, physical_size: Size<u32>) {
|
||||
unsafe {
|
||||
self.gl.viewport(
|
||||
0,
|
||||
0,
|
||||
physical_size.width as i32,
|
||||
physical_size.height as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_information(&self) -> compositor::Information {
|
||||
let adapter = unsafe { self.gl.get_parameter_string(glow::RENDERER) };
|
||||
|
||||
compositor::Information {
|
||||
backend: format!("{:?}", self.gl.version()),
|
||||
adapter,
|
||||
}
|
||||
}
|
||||
|
||||
fn present<T: AsRef<str>>(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
viewport: &Viewport,
|
||||
color: Color,
|
||||
overlay: &[T],
|
||||
) {
|
||||
let gl = &self.gl;
|
||||
|
||||
let [r, g, b, a] = color.into_linear();
|
||||
|
||||
unsafe {
|
||||
gl.clear_color(r, g, b, a);
|
||||
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
renderer.with_primitives(|backend, primitive| {
|
||||
backend.present(gl, primitive, viewport, overlay);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -26,9 +26,6 @@ dds = ["image_rs/dds"]
|
|||
farbfeld = ["image_rs/farbfeld"]
|
||||
canvas = ["lyon"]
|
||||
qr_code = ["qrcode", "canvas"]
|
||||
font-source = ["font-kit"]
|
||||
font-fallback = []
|
||||
font-icons = []
|
||||
opengl = []
|
||||
image_rs = ["kamadak-exif"]
|
||||
|
||||
|
|
@ -60,10 +57,6 @@ version = "0.12"
|
|||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.font-kit]
|
||||
version = "0.10"
|
||||
optional = true
|
||||
|
||||
[dependencies.image_rs]
|
||||
version = "0.24"
|
||||
package = "image"
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
//! Find system fonts or use the built-in ones.
|
||||
#[cfg(feature = "font-source")]
|
||||
mod source;
|
||||
|
||||
#[cfg(feature = "font-source")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))]
|
||||
pub use source::Source;
|
||||
|
||||
#[cfg(feature = "font-source")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))]
|
||||
pub use font_kit::{
|
||||
error::SelectionError as LoadError, family_name::FamilyName as Family,
|
||||
};
|
||||
|
||||
/// A built-in fallback font, for convenience.
|
||||
#[cfg(feature = "font-fallback")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "font-fallback")))]
|
||||
pub const FALLBACK: &[u8] = include_bytes!("../fonts/Lato-Regular.ttf");
|
||||
|
||||
/// A built-in icon font, for convenience.
|
||||
#[cfg(feature = "font-icons")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))]
|
||||
pub const ICONS: iced_native::Font = iced_native::Font::External {
|
||||
name: "iced_wgpu icons",
|
||||
bytes: include_bytes!("../fonts/Icons.ttf"),
|
||||
};
|
||||
|
||||
/// The `char` representing a ✔ icon in the built-in [`ICONS`] font.
|
||||
#[cfg(feature = "font-icons")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))]
|
||||
pub const CHECKMARK_ICON: char = '\u{F00C}';
|
||||
|
||||
/// The `char` representing a ▼ icon in the built-in [`ICONS`] font.
|
||||
#[cfg(feature = "font-icons")]
|
||||
pub const ARROW_DOWN_ICON: char = '\u{E800}';
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
use crate::font::{Family, LoadError};
|
||||
|
||||
/// A font source that can find and load system fonts.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Source {
|
||||
raw: font_kit::source::SystemSource,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
/// Creates a new [`Source`].
|
||||
pub fn new() -> Self {
|
||||
Source {
|
||||
raw: font_kit::source::SystemSource::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds and loads a font matching the set of provided family priorities.
|
||||
pub fn load(&self, families: &[Family]) -> Result<Vec<u8>, LoadError> {
|
||||
let font = self.raw.select_best_match(
|
||||
families,
|
||||
&font_kit::properties::Properties::default(),
|
||||
)?;
|
||||
|
||||
match font {
|
||||
font_kit::handle::Handle::Path { path, .. } => {
|
||||
use std::io::Read;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut reader = std::fs::File::open(path).expect("Read font");
|
||||
let _ = reader.read_to_end(&mut buf);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
font_kit::handle::Handle::Memory { bytes, .. } => {
|
||||
Ok(bytes.as_ref().clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Source {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@ mod transformation;
|
|||
mod viewport;
|
||||
|
||||
pub mod backend;
|
||||
pub mod font;
|
||||
pub mod gradient;
|
||||
pub mod image;
|
||||
pub mod layer;
|
||||
|
|
|
|||
|
|
@ -23,14 +23,11 @@ dds = ["iced_graphics/dds"]
|
|||
farbfeld = ["iced_graphics/farbfeld"]
|
||||
canvas = ["iced_graphics/canvas"]
|
||||
qr_code = ["iced_graphics/qr_code"]
|
||||
default_system_font = ["iced_graphics/font-source"]
|
||||
spirv = ["wgpu/spirv"]
|
||||
webgl = ["wgpu/webgl"]
|
||||
|
||||
[dependencies]
|
||||
wgpu = "0.14"
|
||||
wgpu_glyph = "0.18"
|
||||
glyph_brush = "0.7"
|
||||
raw-window-handle = "0.5"
|
||||
log = "0.4"
|
||||
guillotiere = "0.6"
|
||||
|
|
@ -48,7 +45,6 @@ path = "../native"
|
|||
[dependencies.iced_graphics]
|
||||
version = "0.7"
|
||||
path = "../graphics"
|
||||
features = ["font-fallback", "font-icons"]
|
||||
|
||||
[dependencies.tracing]
|
||||
version = "0.1.6"
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ use crate::triangle;
|
|||
use crate::{Settings, Transformation};
|
||||
|
||||
use iced_graphics::backend;
|
||||
use iced_graphics::font;
|
||||
use iced_graphics::layer::Layer;
|
||||
use iced_graphics::{Primitive, Viewport};
|
||||
use iced_native::alignment;
|
||||
use iced_native::{Font, Size};
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
|
|
@ -173,83 +171,11 @@ impl Backend {
|
|||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
for text in layer.text.iter() {
|
||||
// Target physical coordinates directly to avoid blurry text
|
||||
let text = wgpu_glyph::Section {
|
||||
// TODO: We `round` here to avoid rerasterizing text when
|
||||
// its position changes slightly. This can make text feel a
|
||||
// bit "jumpy". We may be able to do better once we improve
|
||||
// our text rendering/caching pipeline.
|
||||
screen_position: (
|
||||
(text.bounds.x * scale_factor).round(),
|
||||
(text.bounds.y * scale_factor).round(),
|
||||
),
|
||||
// TODO: Fix precision issues with some scale factors.
|
||||
//
|
||||
// The `ceil` here can cause some words to render on the
|
||||
// same line when they should not.
|
||||
//
|
||||
// Ideally, `wgpu_glyph` should be able to compute layout
|
||||
// using logical positions, and then apply the proper
|
||||
// scaling when rendering. This would ensure that both
|
||||
// measuring and rendering follow the same layout rules.
|
||||
bounds: (
|
||||
(text.bounds.width * scale_factor).ceil(),
|
||||
(text.bounds.height * scale_factor).ceil(),
|
||||
),
|
||||
text: vec![wgpu_glyph::Text {
|
||||
text: text.content,
|
||||
scale: wgpu_glyph::ab_glyph::PxScale {
|
||||
x: text.size * scale_factor,
|
||||
y: text.size * scale_factor,
|
||||
},
|
||||
font_id: self.text_pipeline.find_font(text.font),
|
||||
extra: wgpu_glyph::Extra {
|
||||
color: text.color,
|
||||
z: 0.0,
|
||||
},
|
||||
}],
|
||||
layout: wgpu_glyph::Layout::default()
|
||||
.h_align(match text.horizontal_alignment {
|
||||
alignment::Horizontal::Left => {
|
||||
wgpu_glyph::HorizontalAlign::Left
|
||||
}
|
||||
alignment::Horizontal::Center => {
|
||||
wgpu_glyph::HorizontalAlign::Center
|
||||
}
|
||||
alignment::Horizontal::Right => {
|
||||
wgpu_glyph::HorizontalAlign::Right
|
||||
}
|
||||
})
|
||||
.v_align(match text.vertical_alignment {
|
||||
alignment::Vertical::Top => {
|
||||
wgpu_glyph::VerticalAlign::Top
|
||||
}
|
||||
alignment::Vertical::Center => {
|
||||
wgpu_glyph::VerticalAlign::Center
|
||||
}
|
||||
alignment::Vertical::Bottom => {
|
||||
wgpu_glyph::VerticalAlign::Bottom
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
self.text_pipeline.queue(text);
|
||||
for _text in layer.text.iter() {
|
||||
// TODO: Queue text sections
|
||||
}
|
||||
|
||||
self.text_pipeline.draw_queued(
|
||||
device,
|
||||
staging_belt,
|
||||
encoder,
|
||||
target,
|
||||
transformation,
|
||||
wgpu_glyph::Region {
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
},
|
||||
);
|
||||
// TODO: Draw queued
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -261,9 +187,9 @@ impl iced_graphics::Backend for Backend {
|
|||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
const ICON_FONT: Font = font::ICONS;
|
||||
const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
|
||||
const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
|
||||
const ICON_FONT: Font = Font::Default; // TODO
|
||||
const CHECKMARK_ICON: char = '✓';
|
||||
const ARROW_DOWN_ICON: char = '▼';
|
||||
|
||||
fn default_size(&self) -> f32 {
|
||||
self.default_text_size
|
||||
|
|
|
|||
262
wgpu/src/text.rs
262
wgpu/src/text.rs
|
|
@ -1,265 +1,39 @@
|
|||
use crate::Transformation;
|
||||
|
||||
use iced_graphics::font;
|
||||
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
use wgpu_glyph::ab_glyph;
|
||||
|
||||
pub use iced_native::text::Hit;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
draw_brush: RefCell<wgpu_glyph::GlyphBrush<()>>,
|
||||
draw_font_map: RefCell<HashMap<String, wgpu_glyph::FontId>>,
|
||||
measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
|
||||
}
|
||||
pub struct Pipeline;
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
format: wgpu::TextureFormat,
|
||||
default_font: Option<&[u8]>,
|
||||
multithreading: bool,
|
||||
_device: &wgpu::Device,
|
||||
_format: wgpu::TextureFormat,
|
||||
_default_font: Option<&[u8]>,
|
||||
_multithreading: bool,
|
||||
) -> Self {
|
||||
let default_font = default_font.map(|slice| slice.to_vec());
|
||||
|
||||
// TODO: Font customization
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
#[cfg(feature = "default_system_font")]
|
||||
let default_font = {
|
||||
default_font.or_else(|| {
|
||||
font::Source::new()
|
||||
.load(&[font::Family::SansSerif, font::Family::Serif])
|
||||
.ok()
|
||||
})
|
||||
};
|
||||
|
||||
let default_font =
|
||||
default_font.unwrap_or_else(|| font::FALLBACK.to_vec());
|
||||
|
||||
let font = ab_glyph::FontArc::try_from_vec(default_font)
|
||||
.unwrap_or_else(|_| {
|
||||
log::warn!(
|
||||
"System font failed to load. Falling back to \
|
||||
embedded font..."
|
||||
);
|
||||
|
||||
ab_glyph::FontArc::try_from_slice(font::FALLBACK)
|
||||
.expect("Load fallback font")
|
||||
});
|
||||
|
||||
let draw_brush_builder =
|
||||
wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
|
||||
.initial_cache_size((2048, 2048))
|
||||
.draw_cache_multithread(multithreading);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
|
||||
|
||||
let draw_brush = draw_brush_builder.build(device, format);
|
||||
|
||||
let measure_brush =
|
||||
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
||||
|
||||
Pipeline {
|
||||
draw_brush: RefCell::new(draw_brush),
|
||||
draw_font_map: RefCell::new(HashMap::new()),
|
||||
measure_brush: RefCell::new(measure_brush),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue(&mut self, section: wgpu_glyph::Section<'_>) {
|
||||
self.draw_brush.borrow_mut().queue(section);
|
||||
}
|
||||
|
||||
pub fn draw_queued(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
staging_belt: &mut wgpu::util::StagingBelt,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
transformation: Transformation,
|
||||
region: wgpu_glyph::Region,
|
||||
) {
|
||||
self.draw_brush
|
||||
.borrow_mut()
|
||||
.draw_queued_with_transform_and_scissoring(
|
||||
device,
|
||||
staging_belt,
|
||||
encoder,
|
||||
target,
|
||||
transformation.into(),
|
||||
region,
|
||||
)
|
||||
.expect("Draw text");
|
||||
Pipeline
|
||||
}
|
||||
|
||||
pub fn measure(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
font: iced_native::Font,
|
||||
bounds: iced_native::Size,
|
||||
_content: &str,
|
||||
_size: f32,
|
||||
_font: iced_native::Font,
|
||||
_bounds: iced_native::Size,
|
||||
) -> (f32, f32) {
|
||||
use wgpu_glyph::GlyphCruncher;
|
||||
|
||||
let wgpu_glyph::FontId(font_id) = self.find_font(font);
|
||||
|
||||
let section = wgpu_glyph::Section {
|
||||
bounds: (bounds.width, bounds.height),
|
||||
text: vec![wgpu_glyph::Text {
|
||||
text: content,
|
||||
scale: size.into(),
|
||||
font_id: wgpu_glyph::FontId(font_id),
|
||||
extra: wgpu_glyph::Extra::default(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(bounds) =
|
||||
self.measure_brush.borrow_mut().glyph_bounds(section)
|
||||
{
|
||||
(bounds.width().ceil(), bounds.height().ceil())
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hit_test(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
font: iced_native::Font,
|
||||
bounds: iced_native::Size,
|
||||
point: iced_native::Point,
|
||||
nearest_only: bool,
|
||||
_content: &str,
|
||||
_size: f32,
|
||||
_font: iced_native::Font,
|
||||
_bounds: iced_native::Size,
|
||||
_point: iced_native::Point,
|
||||
_nearest_only: bool,
|
||||
) -> Option<Hit> {
|
||||
use wgpu_glyph::GlyphCruncher;
|
||||
|
||||
let wgpu_glyph::FontId(font_id) = self.find_font(font);
|
||||
|
||||
let section = wgpu_glyph::Section {
|
||||
bounds: (bounds.width, bounds.height),
|
||||
text: vec![wgpu_glyph::Text {
|
||||
text: content,
|
||||
scale: size.into(),
|
||||
font_id: wgpu_glyph::FontId(font_id),
|
||||
extra: wgpu_glyph::Extra::default(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut mb = self.measure_brush.borrow_mut();
|
||||
|
||||
// The underlying type is FontArc, so clones are cheap.
|
||||
use wgpu_glyph::ab_glyph::{Font, ScaleFont};
|
||||
let font = mb.fonts()[font_id].clone().into_scaled(size);
|
||||
|
||||
// Implements an iterator over the glyph bounding boxes.
|
||||
let bounds = mb.glyphs(section).map(
|
||||
|wgpu_glyph::SectionGlyph {
|
||||
byte_index, glyph, ..
|
||||
}| {
|
||||
(
|
||||
*byte_index,
|
||||
iced_native::Rectangle::new(
|
||||
iced_native::Point::new(
|
||||
glyph.position.x - font.h_side_bearing(glyph.id),
|
||||
glyph.position.y - font.ascent(),
|
||||
),
|
||||
iced_native::Size::new(
|
||||
font.h_advance(glyph.id),
|
||||
font.ascent() - font.descent(),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Implements computation of the character index based on the byte index
|
||||
// within the input string.
|
||||
let char_index = |byte_index| {
|
||||
let mut b_count = 0;
|
||||
for (i, utf8_len) in
|
||||
content.chars().map(|c| c.len_utf8()).enumerate()
|
||||
{
|
||||
if byte_index < (b_count + utf8_len) {
|
||||
return i;
|
||||
}
|
||||
b_count += utf8_len;
|
||||
None
|
||||
}
|
||||
|
||||
byte_index
|
||||
};
|
||||
|
||||
if !nearest_only {
|
||||
for (idx, bounds) in bounds.clone() {
|
||||
if bounds.contains(point) {
|
||||
return Some(Hit::CharOffset(char_index(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nearest = bounds
|
||||
.map(|(index, bounds)| (index, bounds.center()))
|
||||
.min_by(|(_, center_a), (_, center_b)| {
|
||||
center_a
|
||||
.distance(point)
|
||||
.partial_cmp(¢er_b.distance(point))
|
||||
.unwrap_or(std::cmp::Ordering::Greater)
|
||||
});
|
||||
|
||||
nearest.map(|(idx, center)| {
|
||||
Hit::NearestCharOffset(char_index(idx), point - center)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn trim_measurement_cache(&mut self) {
|
||||
// TODO: We should probably use a `GlyphCalculator` for this. However,
|
||||
// it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
|
||||
// This makes stuff quite inconvenient. A manual method for trimming the
|
||||
// cache would make our lives easier.
|
||||
loop {
|
||||
let action = self
|
||||
.measure_brush
|
||||
.borrow_mut()
|
||||
.process_queued(|_, _| {}, |_| {});
|
||||
|
||||
match action {
|
||||
Ok(_) => break,
|
||||
Err(glyph_brush::BrushError::TextureTooSmall { suggested }) => {
|
||||
let (width, height) = suggested;
|
||||
|
||||
self.measure_brush
|
||||
.borrow_mut()
|
||||
.resize_texture(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_font(&self, font: iced_native::Font) -> wgpu_glyph::FontId {
|
||||
match font {
|
||||
iced_native::Font::Default => wgpu_glyph::FontId(0),
|
||||
iced_native::Font::External { name, bytes } => {
|
||||
if let Some(font_id) = self.draw_font_map.borrow().get(name) {
|
||||
return *font_id;
|
||||
}
|
||||
|
||||
let font = ab_glyph::FontArc::try_from_slice(bytes)
|
||||
.expect("Load font");
|
||||
|
||||
let _ = self.measure_brush.borrow_mut().add_font(font.clone());
|
||||
|
||||
let font_id = self.draw_brush.borrow_mut().add_font(font);
|
||||
|
||||
let _ = self
|
||||
.draw_font_map
|
||||
.borrow_mut()
|
||||
.insert(String::from(name), font_id);
|
||||
|
||||
font_id
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn trim_measurement_cache(&mut self) {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue