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]
|
[features]
|
||||||
default = ["wgpu"]
|
default = ["wgpu"]
|
||||||
# Enables the `Image` widget
|
# Enables the `Image` widget
|
||||||
image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"]
|
image = ["iced_wgpu?/image", "image_rs"]
|
||||||
# Enables the `Svg` widget
|
# Enables the `Svg` widget
|
||||||
svg = ["iced_wgpu?/svg", "iced_glow?/svg"]
|
svg = ["iced_wgpu?/svg"]
|
||||||
# Enables the `Canvas` widget
|
# Enables the `Canvas` widget
|
||||||
canvas = ["iced_graphics/canvas"]
|
canvas = ["iced_graphics/canvas"]
|
||||||
# Enables the `QRCode` widget
|
# Enables the `QRCode` widget
|
||||||
qr_code = ["iced_graphics/qr_code"]
|
qr_code = ["iced_graphics/qr_code"]
|
||||||
# Enables the `iced_wgpu` renderer
|
# Enables the `iced_wgpu` renderer
|
||||||
wgpu = ["iced_wgpu"]
|
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)
|
# Enables a debug view in native platforms (press F12)
|
||||||
debug = ["iced_winit/debug"]
|
debug = ["iced_winit/debug"]
|
||||||
# Enables `tokio` as the `executor::Default` on native platforms
|
# Enables `tokio` as the `executor::Default` on native platforms
|
||||||
|
|
@ -44,7 +40,6 @@ chrome-trace = [
|
||||||
"iced_winit/chrome-trace",
|
"iced_winit/chrome-trace",
|
||||||
"iced_glutin?/trace",
|
"iced_glutin?/trace",
|
||||||
"iced_wgpu?/tracing",
|
"iced_wgpu?/tracing",
|
||||||
"iced_glow?/tracing",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
|
|
@ -55,7 +50,6 @@ members = [
|
||||||
"core",
|
"core",
|
||||||
"futures",
|
"futures",
|
||||||
"graphics",
|
"graphics",
|
||||||
"glow",
|
|
||||||
"glutin",
|
"glutin",
|
||||||
"lazy",
|
"lazy",
|
||||||
"native",
|
"native",
|
||||||
|
|
@ -72,7 +66,6 @@ iced_native = { version = "0.9", path = "native" }
|
||||||
iced_graphics = { version = "0.7", path = "graphics" }
|
iced_graphics = { version = "0.7", path = "graphics" }
|
||||||
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
|
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
|
||||||
iced_glutin = { version = "0.7", path = "glutin", optional = true }
|
iced_glutin = { version = "0.7", path = "glutin", optional = true }
|
||||||
iced_glow = { version = "0.7", path = "glow", optional = true }
|
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
|
||||||
[dependencies.image_rs]
|
[dependencies.image_rs]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "integration_wgpu"
|
name = "integration"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
<h1>integration_wgpu</h1>
|
<h1>integration_wgpu</h1>
|
||||||
<canvas id="iced_canvas"></canvas>
|
<canvas id="iced_canvas"></canvas>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import init from "./integration_wgpu.js";
|
import init from "./integration.js";
|
||||||
init('./integration_wgpu_bg.wasm');
|
init('./integration_bg.wasm');
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
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"]
|
farbfeld = ["image_rs/farbfeld"]
|
||||||
canvas = ["lyon"]
|
canvas = ["lyon"]
|
||||||
qr_code = ["qrcode", "canvas"]
|
qr_code = ["qrcode", "canvas"]
|
||||||
font-source = ["font-kit"]
|
|
||||||
font-fallback = []
|
|
||||||
font-icons = []
|
|
||||||
opengl = []
|
opengl = []
|
||||||
image_rs = ["kamadak-exif"]
|
image_rs = ["kamadak-exif"]
|
||||||
|
|
||||||
|
|
@ -60,10 +57,6 @@ version = "0.12"
|
||||||
optional = true
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.font-kit]
|
|
||||||
version = "0.10"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.image_rs]
|
[dependencies.image_rs]
|
||||||
version = "0.24"
|
version = "0.24"
|
||||||
package = "image"
|
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;
|
mod viewport;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod font;
|
|
||||||
pub mod gradient;
|
pub mod gradient;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod layer;
|
pub mod layer;
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,11 @@ dds = ["iced_graphics/dds"]
|
||||||
farbfeld = ["iced_graphics/farbfeld"]
|
farbfeld = ["iced_graphics/farbfeld"]
|
||||||
canvas = ["iced_graphics/canvas"]
|
canvas = ["iced_graphics/canvas"]
|
||||||
qr_code = ["iced_graphics/qr_code"]
|
qr_code = ["iced_graphics/qr_code"]
|
||||||
default_system_font = ["iced_graphics/font-source"]
|
|
||||||
spirv = ["wgpu/spirv"]
|
spirv = ["wgpu/spirv"]
|
||||||
webgl = ["wgpu/webgl"]
|
webgl = ["wgpu/webgl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wgpu = "0.14"
|
wgpu = "0.14"
|
||||||
wgpu_glyph = "0.18"
|
|
||||||
glyph_brush = "0.7"
|
|
||||||
raw-window-handle = "0.5"
|
raw-window-handle = "0.5"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
guillotiere = "0.6"
|
guillotiere = "0.6"
|
||||||
|
|
@ -48,7 +45,6 @@ path = "../native"
|
||||||
[dependencies.iced_graphics]
|
[dependencies.iced_graphics]
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
path = "../graphics"
|
path = "../graphics"
|
||||||
features = ["font-fallback", "font-icons"]
|
|
||||||
|
|
||||||
[dependencies.tracing]
|
[dependencies.tracing]
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,8 @@ use crate::triangle;
|
||||||
use crate::{Settings, Transformation};
|
use crate::{Settings, Transformation};
|
||||||
|
|
||||||
use iced_graphics::backend;
|
use iced_graphics::backend;
|
||||||
use iced_graphics::font;
|
|
||||||
use iced_graphics::layer::Layer;
|
use iced_graphics::layer::Layer;
|
||||||
use iced_graphics::{Primitive, Viewport};
|
use iced_graphics::{Primitive, Viewport};
|
||||||
use iced_native::alignment;
|
|
||||||
use iced_native::{Font, Size};
|
use iced_native::{Font, Size};
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
|
|
@ -173,83 +171,11 @@ impl Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !layer.text.is_empty() {
|
if !layer.text.is_empty() {
|
||||||
for text in layer.text.iter() {
|
for _text in layer.text.iter() {
|
||||||
// Target physical coordinates directly to avoid blurry text
|
// TODO: Queue text sections
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.text_pipeline.draw_queued(
|
// TODO: Draw queued
|
||||||
device,
|
|
||||||
staging_belt,
|
|
||||||
encoder,
|
|
||||||
target,
|
|
||||||
transformation,
|
|
||||||
wgpu_glyph::Region {
|
|
||||||
x: bounds.x,
|
|
||||||
y: bounds.y,
|
|
||||||
width: bounds.width,
|
|
||||||
height: bounds.height,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,9 +187,9 @@ impl iced_graphics::Backend for Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl backend::Text for Backend {
|
impl backend::Text for Backend {
|
||||||
const ICON_FONT: Font = font::ICONS;
|
const ICON_FONT: Font = Font::Default; // TODO
|
||||||
const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
|
const CHECKMARK_ICON: char = '✓';
|
||||||
const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
|
const ARROW_DOWN_ICON: char = '▼';
|
||||||
|
|
||||||
fn default_size(&self) -> f32 {
|
fn default_size(&self) -> f32 {
|
||||||
self.default_text_size
|
self.default_text_size
|
||||||
|
|
|
||||||
264
wgpu/src/text.rs
264
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;
|
pub use iced_native::text::Hit;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline;
|
||||||
draw_brush: RefCell<wgpu_glyph::GlyphBrush<()>>,
|
|
||||||
draw_font_map: RefCell<HashMap<String, wgpu_glyph::FontId>>,
|
|
||||||
measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &wgpu::Device,
|
_device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
_format: wgpu::TextureFormat,
|
||||||
default_font: Option<&[u8]>,
|
_default_font: Option<&[u8]>,
|
||||||
multithreading: bool,
|
_multithreading: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let default_font = default_font.map(|slice| slice.to_vec());
|
Pipeline
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn measure(
|
pub fn measure(
|
||||||
&self,
|
&self,
|
||||||
content: &str,
|
_content: &str,
|
||||||
size: f32,
|
_size: f32,
|
||||||
font: iced_native::Font,
|
_font: iced_native::Font,
|
||||||
bounds: iced_native::Size,
|
_bounds: iced_native::Size,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
use wgpu_glyph::GlyphCruncher;
|
(0.0, 0.0)
|
||||||
|
|
||||||
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(
|
pub fn hit_test(
|
||||||
&self,
|
&self,
|
||||||
content: &str,
|
_content: &str,
|
||||||
size: f32,
|
_size: f32,
|
||||||
font: iced_native::Font,
|
_font: iced_native::Font,
|
||||||
bounds: iced_native::Size,
|
_bounds: iced_native::Size,
|
||||||
point: iced_native::Point,
|
_point: iced_native::Point,
|
||||||
nearest_only: bool,
|
_nearest_only: bool,
|
||||||
) -> Option<Hit> {
|
) -> Option<Hit> {
|
||||||
use wgpu_glyph::GlyphCruncher;
|
None
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue