mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-26 21:58:20 -06:00
kinda got text rendering working as well as multi draw call
This commit is contained in:
parent
a6bb48dddd
commit
b120e0449b
70
kubi-ui-examples/examples/text.rs
Normal file
70
kubi-ui-examples/examples/text.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
use glam::{UVec2, vec4};
|
||||||
|
use glium::{backend::glutin::SimpleWindowBuilder, Surface};
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::{EventLoopBuilder, ControlFlow}
|
||||||
|
};
|
||||||
|
use kubi_ui::{
|
||||||
|
KubiUi,
|
||||||
|
element::{
|
||||||
|
UiElement,
|
||||||
|
progress_bar::ProgressBar,
|
||||||
|
container::{Container, Sides, Alignment},
|
||||||
|
rect::Rect, text::Text
|
||||||
|
},
|
||||||
|
interaction::IntoInteractable,
|
||||||
|
UiSize,
|
||||||
|
UiDirection, IfModified,
|
||||||
|
};
|
||||||
|
use kubi_ui_glium::GliumUiRenderer;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
kubi_logging::init();
|
||||||
|
|
||||||
|
let event_loop = EventLoopBuilder::new().build().unwrap();
|
||||||
|
let (window, display) = SimpleWindowBuilder::new().build(&event_loop);
|
||||||
|
|
||||||
|
let mut kui = KubiUi::new();
|
||||||
|
let mut backend = GliumUiRenderer::new(&display);
|
||||||
|
|
||||||
|
let instant = Instant::now();
|
||||||
|
event_loop.run(|event, window_target| {
|
||||||
|
window_target.set_control_flow(ControlFlow::Poll);
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||||
|
window_target.exit();
|
||||||
|
},
|
||||||
|
Event::AboutToWait => {
|
||||||
|
let mut frame = display.draw();
|
||||||
|
frame.clear_color_srgb(0.5, 0.5, 0.5, 0.);
|
||||||
|
|
||||||
|
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||||
|
|
||||||
|
kui.begin();
|
||||||
|
|
||||||
|
kui.add(Container {
|
||||||
|
gap: 5.,
|
||||||
|
padding: Sides::all(5.),
|
||||||
|
align: (Alignment::Begin, Alignment::Begin),
|
||||||
|
size: (UiSize::Percentage(1.), UiSize::Percentage(1.)),
|
||||||
|
elements: vec![
|
||||||
|
Box::new(Text {
|
||||||
|
text: "Heloworld!Loremipsumsimdoloramet".into(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
}, resolution);
|
||||||
|
|
||||||
|
kui.end();
|
||||||
|
|
||||||
|
backend.update(&kui);
|
||||||
|
backend.draw(&mut frame, resolution);
|
||||||
|
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
}
|
|
@ -1,11 +1,21 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
precision highp sampler2D;
|
||||||
|
|
||||||
out vec4 out_color;
|
out vec4 out_color;
|
||||||
in vec4 vtx_color;
|
in vec4 vtx_color;
|
||||||
|
in vec2 vtx_uv;
|
||||||
|
uniform bool use_tex;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (vtx_color.w <= 0.) discard;
|
//if (vtx_color.w <= 0.) discard;
|
||||||
out_color = vtx_color;
|
vec4 tex_color;
|
||||||
|
if (use_tex) {
|
||||||
|
tex_color = texture(tex, vtx_uv);
|
||||||
|
} else {
|
||||||
|
tex_color = vec4(1.);
|
||||||
|
}
|
||||||
|
out_color = tex_color * vtx_color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
uniform vec2 resolution;
|
uniform vec2 resolution;
|
||||||
|
in vec2 uv;
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
in vec2 position;
|
in vec2 position;
|
||||||
out vec4 vtx_color;
|
out vec4 vtx_color;
|
||||||
|
out vec2 vtx_uv;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vtx_color = color;
|
vtx_color = color;
|
||||||
|
vtx_uv = uv;
|
||||||
vec2 pos2d = (vec2(2., -2.) * (position / resolution)) + vec2(-1, 1);
|
vec2 pos2d = (vec2(2., -2.) * (position / resolution)) + vec2(-1, 1);
|
||||||
gl_Position = vec4(pos2d, 0., 1.);
|
gl_Position = vec4(pos2d, 0., 1.);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
use std::rc::Rc;
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use glium::{
|
use glium::{
|
||||||
Surface, DrawParameters, Blend,
|
Surface, DrawParameters, Blend,
|
||||||
Program, VertexBuffer, IndexBuffer,
|
Program, VertexBuffer, IndexBuffer,
|
||||||
backend::Facade,
|
backend::{Facade, Context},
|
||||||
|
texture::{SrgbTexture2d, RawImage2d},
|
||||||
index::PrimitiveType,
|
index::PrimitiveType,
|
||||||
implement_vertex, uniform, texture::{SrgbTexture2d, RawImage2d},
|
implement_vertex,
|
||||||
|
uniform, uniforms::DynamicUniforms,
|
||||||
};
|
};
|
||||||
use kubi_ui::{
|
use kubi_ui::{
|
||||||
KubiUi,
|
KubiUi,
|
||||||
draw::{UiDrawPlan, UiVertex},
|
draw::{UiDrawPlan, UiVertex, BindTexture},
|
||||||
text::FontTextureInfo, IfModified,
|
text::FontTextureInfo, IfModified,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,10 +39,10 @@ impl From<UiVertex> for Vertex {
|
||||||
implement_vertex!(Vertex, position, color, uv);
|
implement_vertex!(Vertex, position, color, uv);
|
||||||
|
|
||||||
struct BufferPair {
|
struct BufferPair {
|
||||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
pub vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||||
index_buffer: glium::IndexBuffer<u32>,
|
pub index_buffer: glium::IndexBuffer<u32>,
|
||||||
vertex_count: usize,
|
pub vertex_count: usize,
|
||||||
index_count: usize,
|
pub index_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferPair {
|
impl BufferPair {
|
||||||
|
@ -102,65 +105,121 @@ impl BufferPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GliumUiRenderer {
|
struct GlDrawCall {
|
||||||
program: glium::Program,
|
active: bool,
|
||||||
buffer: BufferPair,
|
buffer: BufferPair,
|
||||||
|
bind_texture: Option<Rc<SrgbTexture2d>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GliumUiRenderer {
|
||||||
|
context: Rc<Context>,
|
||||||
|
program: glium::Program,
|
||||||
|
font_texture: Option<Rc<SrgbTexture2d>>,
|
||||||
|
plan: Vec<GlDrawCall>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GliumUiRenderer {
|
impl GliumUiRenderer {
|
||||||
pub fn new<F: Facade>(facade: &F) -> Self {
|
pub fn new<F: Facade>(facade: &F) -> Self {
|
||||||
log::info!("init glium backend for ui");
|
log::info!("init glium backend for kui");
|
||||||
log::debug!("init program");
|
|
||||||
let program = Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap();
|
|
||||||
Self {
|
Self {
|
||||||
program,
|
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
||||||
buffer: BufferPair::new(facade)
|
context: Rc::clone(facade.get_context()),
|
||||||
|
font_texture: None,
|
||||||
|
plan: vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_draw_plan(&mut self, plan: &UiDrawPlan) {
|
pub fn update_draw_plan(&mut self, plan: &UiDrawPlan) {
|
||||||
assert!(plan.calls.len() == 1, "multiple draw calls not supported yet");
|
if plan.calls.len() > self.plan.len() {
|
||||||
let data_vtx = &plan.calls[0].vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>();
|
self.plan.resize_with(plan.calls.len(), || {
|
||||||
let data_idx = &plan.calls[0].indices;
|
GlDrawCall {
|
||||||
self.buffer.write_data(data_vtx, data_idx);
|
buffer: BufferPair::new(&self.context),
|
||||||
|
bind_texture: None,
|
||||||
|
active: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for step in &mut self.plan[plan.calls.len()..] {
|
||||||
|
step.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (idx, call) in plan.calls.iter().enumerate() {
|
||||||
|
let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..];
|
||||||
|
let data_idx = &call.indices[..];
|
||||||
|
self.plan[idx].active = true;
|
||||||
|
self.plan[idx].buffer.write_data(data_vtx, data_idx);
|
||||||
|
self.plan[idx].bind_texture = match call.bind_texture {
|
||||||
|
Some(BindTexture::FontTexture) => {
|
||||||
|
const NO_FNT_TEX: &str = "Font texture exists in draw plan but not yet inited. Make sure to call update_font_texture() *before* update_draw_plan()";
|
||||||
|
Some(Rc::clone(self.font_texture.as_ref().expect(NO_FNT_TEX)))
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_font_texture(&mut self, font_texture: &FontTextureInfo) {
|
pub fn update_font_texture(&mut self, font_texture: &FontTextureInfo) {
|
||||||
//HACK: get context from buffer
|
log::debug!("updating font texture");
|
||||||
let ctx = self.buffer.index_buffer.get_context();
|
self.font_texture = Some(Rc::new(SrgbTexture2d::new(
|
||||||
SrgbTexture2d::new(ctx, RawImage2d::from_raw_rgb(
|
&self.context,
|
||||||
font_texture.data.to_owned(),
|
RawImage2d::from_raw_rgba(
|
||||||
(font_texture.size.x, font_texture.size.y)
|
font_texture.data.to_owned(),
|
||||||
)).unwrap();
|
(font_texture.size.x, font_texture.size.y)
|
||||||
|
)
|
||||||
|
).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, kui: &KubiUi) {
|
pub fn update(&mut self, kui: &KubiUi) {
|
||||||
if let Some(plan) = kui.draw_plan().if_modified() {
|
|
||||||
self.update_draw_plan(plan);
|
|
||||||
}
|
|
||||||
if let Some(texture) = kui.font_texture().if_modified() {
|
if let Some(texture) = kui.font_texture().if_modified() {
|
||||||
self.update_font_texture(texture);
|
self.update_font_texture(texture);
|
||||||
}
|
}
|
||||||
|
if let Some(plan) = kui.draw_plan().if_modified() {
|
||||||
|
self.update_draw_plan(plan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
|
pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
|
||||||
if self.buffer.is_empty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = DrawParameters {
|
let params = DrawParameters {
|
||||||
blend: Blend::alpha_blending(),
|
blend: Blend::alpha_blending(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
frame.draw(
|
for step in &self.plan {
|
||||||
self.buffer.vertex_buffer.slice(0..self.buffer.vertex_count).unwrap(),
|
if !step.active {
|
||||||
self.buffer.index_buffer.slice(0..self.buffer.index_count).unwrap(),
|
continue
|
||||||
&self.program,
|
}
|
||||||
&uniform! {
|
|
||||||
resolution: resolution.to_array(),
|
if step.buffer.is_empty() {
|
||||||
},
|
continue
|
||||||
¶ms,
|
}
|
||||||
).unwrap();
|
|
||||||
|
let vtx_buffer = step.buffer.vertex_buffer.slice(0..step.buffer.vertex_count).unwrap();
|
||||||
|
let idx_buffer = step.buffer.index_buffer.slice(0..step.buffer.index_count).unwrap();
|
||||||
|
|
||||||
|
if let Some(bind_texture) = step.bind_texture.as_ref() {
|
||||||
|
frame.draw(
|
||||||
|
vtx_buffer,
|
||||||
|
idx_buffer,
|
||||||
|
&self.program,
|
||||||
|
&uniform! {
|
||||||
|
resolution: resolution.to_array(),
|
||||||
|
tex: bind_texture.as_ref(),
|
||||||
|
use_tex: true,
|
||||||
|
},
|
||||||
|
¶ms,
|
||||||
|
).unwrap();
|
||||||
|
} else {
|
||||||
|
frame.draw(
|
||||||
|
vtx_buffer,
|
||||||
|
idx_buffer,
|
||||||
|
&self.program,
|
||||||
|
&uniform! {
|
||||||
|
resolution: resolution.to_array(),
|
||||||
|
use_tex: false,
|
||||||
|
},
|
||||||
|
¶ms,
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{IfModified, text::TextRenderer};
|
use crate::{IfModified, text::{TextRenderer, FontHandle}};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use glam::{Vec2, Vec4, vec2};
|
use glam::{Vec2, Vec4, vec2};
|
||||||
|
@ -63,22 +63,75 @@ pub struct UiDrawPlan {
|
||||||
pub calls: Vec<UiDrawCall>
|
pub calls: Vec<UiDrawCall>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CallSwapper {
|
||||||
|
calls: Vec<UiDrawCall>,
|
||||||
|
call: UiDrawCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallSwapper {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
calls: vec![],
|
||||||
|
call: UiDrawCall::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current(&self) -> &UiDrawCall {
|
||||||
|
&self.call
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_mut(&mut self) -> &mut UiDrawCall {
|
||||||
|
&mut self.call
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap(&mut self) {
|
||||||
|
self.calls.push(std::mem::replace(&mut self.call, UiDrawCall::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(mut self) -> Vec<UiDrawCall> {
|
||||||
|
self.calls.push(self.call);
|
||||||
|
self.calls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UiDrawPlan {
|
impl UiDrawPlan {
|
||||||
pub fn build(calls: &UiDrawCommands, tr: &mut TextRenderer) -> Self {
|
pub fn build(draw_commands: &UiDrawCommands, tr: &mut TextRenderer) -> Self {
|
||||||
let mut call = UiDrawCall::default();
|
let mut swapper = CallSwapper::new();
|
||||||
for command in &calls.commands {
|
let mut prev_command = None;
|
||||||
|
for command in &draw_commands.commands {
|
||||||
|
|
||||||
|
let do_swap = if let Some(prev_command) = prev_command {
|
||||||
|
std::mem::discriminant(prev_command) != std::mem::discriminant(command)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if do_swap {
|
||||||
|
swapper.swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if do_swap || prev_command.is_none() {
|
||||||
|
match command {
|
||||||
|
UiDrawCommand::Rectangle { .. } => (),
|
||||||
|
UiDrawCommand::Text { .. } => {
|
||||||
|
swapper.current_mut().bind_texture = Some(BindTexture::FontTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let vidx = swapper.current().vertices.len() as u32;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
UiDrawCommand::Rectangle { position, size, color } => {
|
UiDrawCommand::Rectangle { position, size, color } => {
|
||||||
let idx = call.vertices.len() as u32;
|
swapper.current_mut().indices.extend([vidx, vidx + 1, vidx + 2, vidx, vidx + 2, vidx + 3]);
|
||||||
call.indices.extend([idx, idx + 1, idx + 2, idx, idx + 2, idx + 3]);
|
swapper.current_mut().vertices.extend([
|
||||||
call.vertices.extend([
|
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position,
|
position: *position,
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(0.0, 0.0),
|
uv: vec2(0.0, 0.0),
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + Vec2::new(size.x, 0.0),
|
position: *position + vec2(size.x, 0.0),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(1.0, 0.0),
|
uv: vec2(1.0, 0.0),
|
||||||
},
|
},
|
||||||
|
@ -88,19 +141,45 @@ impl UiDrawPlan {
|
||||||
uv: vec2(1.0, 1.0),
|
uv: vec2(1.0, 1.0),
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + Vec2::new(0.0, size.y),
|
position: *position + vec2(0.0, size.y),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(0.0, 1.0),
|
uv: vec2(0.0, 1.0),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
UiDrawCommand::Text { position, size, color, text } => {
|
UiDrawCommand::Text { position, size, color, text } => {
|
||||||
todo!()
|
for char in text.chars() {
|
||||||
|
tr.glyph(FontHandle(0), char, *size);
|
||||||
|
}
|
||||||
|
swapper.current_mut().indices.extend([vidx, vidx + 1, vidx + 2, vidx, vidx + 2, vidx + 3]);
|
||||||
|
swapper.current_mut().vertices.extend([
|
||||||
|
UiVertex {
|
||||||
|
position: *position,
|
||||||
|
color: *color,
|
||||||
|
uv: vec2(0.0, 0.0),
|
||||||
|
},
|
||||||
|
UiVertex {
|
||||||
|
position: *position + vec2(32., 0.0),
|
||||||
|
color: *color,
|
||||||
|
uv: vec2(1.0, 0.0),
|
||||||
|
},
|
||||||
|
UiVertex {
|
||||||
|
position: *position + vec2(32., 32.),
|
||||||
|
color: *color,
|
||||||
|
uv: vec2(1.0, 1.0),
|
||||||
|
},
|
||||||
|
UiVertex {
|
||||||
|
position: *position + vec2(0.0, 32.),
|
||||||
|
color: *color,
|
||||||
|
uv: vec2(0.0, 1.0),
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
prev_command = Some(command);
|
||||||
}
|
}
|
||||||
Self {
|
Self {
|
||||||
calls: vec![call]
|
calls: swapper.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod builtin {
|
||||||
pub mod container;
|
pub mod container;
|
||||||
pub mod spacer;
|
pub mod spacer;
|
||||||
pub mod progress_bar;
|
pub mod progress_bar;
|
||||||
|
pub mod text;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "builtin_elements")]
|
||||||
|
|
56
kubi-ui/src/element/builtin/text.rs
Normal file
56
kubi-ui/src/element/builtin/text.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use glam::{vec2, Vec4};
|
||||||
|
use crate::{
|
||||||
|
LayoutInfo,
|
||||||
|
UiSize,
|
||||||
|
element::UiElement,
|
||||||
|
state::StateRepo,
|
||||||
|
measure::Response,
|
||||||
|
draw::UiDrawCommand
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Text {
|
||||||
|
pub text: Cow<'static, str>,
|
||||||
|
pub size: (UiSize, UiSize),
|
||||||
|
pub color: Vec4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Text {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
text: "".into(),
|
||||||
|
size: (UiSize::Percentage(1.), UiSize::Pixels(32.)),
|
||||||
|
color: Vec4::new(1., 1., 1., 1.),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UiElement for Text {
|
||||||
|
fn measure(&self, _state: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||||
|
Response {
|
||||||
|
size: vec2(
|
||||||
|
match self.size.0 {
|
||||||
|
UiSize::Auto => layout.max_size.x,
|
||||||
|
UiSize::Percentage(percentage) => layout.max_size.x * percentage,
|
||||||
|
UiSize::Pixels(pixels) => pixels,
|
||||||
|
},
|
||||||
|
match self.size.1 {
|
||||||
|
UiSize::Auto => layout.max_size.y,
|
||||||
|
UiSize::Percentage(percentage) => layout.max_size.y * percentage,
|
||||||
|
UiSize::Pixels(pixels) => pixels,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
hints: Default::default(),
|
||||||
|
user_data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(&self, _measure: &Response, _state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCommand>) {
|
||||||
|
draw.push(UiDrawCommand::Text {
|
||||||
|
text: self.text.clone(),
|
||||||
|
position: layout.position,
|
||||||
|
size: 32,
|
||||||
|
color: self.color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ impl FontTextureManager {
|
||||||
FontTextureManager {
|
FontTextureManager {
|
||||||
glyph_cache: HashMap::new(),
|
glyph_cache: HashMap::new(),
|
||||||
packer: DensePacker::new(size.x as i32, size.y as i32),
|
packer: DensePacker::new(size.x as i32, size.y as i32),
|
||||||
font_texture: vec![0; (size.x * size.y) as usize],
|
font_texture: vec![0; (size.x * size.y * 4) as usize],
|
||||||
font_texture_size: size,
|
font_texture_size: size,
|
||||||
modified: false,
|
modified: false,
|
||||||
}
|
}
|
||||||
|
@ -79,17 +79,18 @@ impl FontTextureManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either looks up the glyph in the cache or renders it and adds it to the cache.
|
/// Either looks up the glyph in the cache or renders it and adds it to the cache.
|
||||||
fn glyph_allocate(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> (bool, Arc<GlyphCacheEntry>) {
|
pub fn glyph(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||||
let key = GlyphCacheKey {
|
let key = GlyphCacheKey {
|
||||||
font_index: font_handle.0,
|
font_index: font_handle.0,
|
||||||
character,
|
character,
|
||||||
size,
|
size,
|
||||||
};
|
};
|
||||||
if let Some(entry) = self.glyph_cache.get(&key) {
|
if let Some(entry) = self.glyph_cache.get(&key) {
|
||||||
return (false, Arc::clone(entry));
|
return Arc::clone(entry);
|
||||||
}
|
}
|
||||||
let font = font_manager.get(font_handle).unwrap();
|
let font = font_manager.get(font_handle).unwrap();
|
||||||
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
||||||
|
log::debug!("rasterized glyph: {:?}, {:?}", metrics, bitmap);
|
||||||
let texture_position = self.packer.pack(metrics.width as i32, metrics.height as i32, false).unwrap();
|
let texture_position = self.packer.pack(metrics.width as i32, metrics.height as i32, false).unwrap();
|
||||||
let texture_size = uvec2(metrics.width as u32, metrics.height as u32);
|
let texture_size = uvec2(metrics.width as u32, metrics.height as u32);
|
||||||
let entry = Arc::new(GlyphCacheEntry {
|
let entry = Arc::new(GlyphCacheEntry {
|
||||||
|
@ -99,30 +100,37 @@ impl FontTextureManager {
|
||||||
size: texture_size,
|
size: texture_size,
|
||||||
});
|
});
|
||||||
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
|
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
|
||||||
(true, entry)
|
self.glyph_place(&entry);
|
||||||
|
self.modified = true;
|
||||||
|
entry
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Place glyph onto the font texture.
|
/// Place glyph onto the font texture.
|
||||||
fn glyph_place(&mut self, entry: &GlyphCacheEntry) {
|
fn glyph_place(&mut self, entry: &GlyphCacheEntry) {
|
||||||
let tex_size = self.font_texture_size;
|
let tex_size = self.font_texture_size;
|
||||||
let GlyphCacheEntry { size, position, .. } = entry;
|
let GlyphCacheEntry { size, position, data, .. } = entry;
|
||||||
|
//println!("{size:?} {position:?}");
|
||||||
for y in 0..size.y {
|
for y in 0..size.y {
|
||||||
for x in 0..size.x {
|
for x in 0..size.x {
|
||||||
let src = (size.x * y + x) as usize;
|
let src = (size.x * y + x) as usize;
|
||||||
let dst = (tex_size.x * (y + position.y as u32) + (x + position.x as u32)) as usize;
|
let dst = (tex_size.x * (y + position.y as u32) + (x + position.x as u32)) as usize * 4;
|
||||||
self.font_texture[dst] = entry.data[src];
|
self.font_texture[dst..=(dst + 3)].copy_from_slice(&[255, 0, 0, data[src]]);
|
||||||
|
self.font_texture[dst] = data[src];
|
||||||
|
//print!("{} ", if data[src] > 0 {'#'} else {'.'});
|
||||||
|
//print!("{src} {dst} / ");
|
||||||
}
|
}
|
||||||
|
//println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn glyph(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
// pub fn glyph(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||||
let (is_new, glyph) = self.glyph_allocate(font_manager, font_handle, character, size);
|
// let (is_new, glyph) = self.glyph_allocate(font_manager, font_handle, character, size);
|
||||||
if is_new {
|
// if is_new {
|
||||||
self.glyph_place(&glyph);
|
// self.glyph_place(&glyph);
|
||||||
self.modified = true;
|
// self.modified = true;
|
||||||
}
|
// }
|
||||||
glyph
|
// glyph
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FontTextureManager {
|
impl Default for FontTextureManager {
|
||||||
|
|
Loading…
Reference in a new issue