318 lines
8.8 KiB
Rust
318 lines
8.8 KiB
Rust
mod esdt;
|
|
use esdt::{Image2d, Unorm8};
|
|
use glow::{HasContext, TEXTURE_2D};
|
|
use std::{cell::RefCell, collections::HashMap};
|
|
use text_items::{LineStyle, Text as TextTrait};
|
|
mod text_items;
|
|
pub use text_items::{Anchor, PositionText, TextLine};
|
|
|
|
use crate::{
|
|
components::{CodeType, Program, Shader},
|
|
errors::*,
|
|
font_manager::{FontManager, FontStyle},
|
|
utils::resources::RcGlTexture,
|
|
};
|
|
|
|
use super::{transforms::viewport::Viewport, AttaWithBuffer, Config, Graphics};
|
|
pub struct Text<'a> {
|
|
gl: &'a glow::Context,
|
|
font_manager: RefCell<FontManager>,
|
|
cache: RefCell<HashMap<String, Cache<'a>>>,
|
|
items: Vec<PositionText>,
|
|
program: Program,
|
|
}
|
|
|
|
pub struct Cache<'a> {
|
|
gl: &'a glow::Context,
|
|
cache: HashMap<char, TextVType>,
|
|
// cache_tex: Vec<u8>,
|
|
width: usize,
|
|
height: usize,
|
|
last_pos: [usize; 2],
|
|
tex: RcGlTexture<'a>,
|
|
}
|
|
|
|
impl<'a> Cache<'a> {
|
|
pub fn new(gl: &'a glow::Context) -> Self {
|
|
let tex = unsafe {
|
|
let tex = gl.create_texture().unwrap();
|
|
gl.bind_texture(glow::TEXTURE_2D, Some(tex));
|
|
gl.tex_parameter_i32(
|
|
glow::TEXTURE_2D,
|
|
glow::TEXTURE_MIN_FILTER,
|
|
glow::LINEAR as i32,
|
|
);
|
|
gl.tex_parameter_i32(
|
|
glow::TEXTURE_2D,
|
|
glow::TEXTURE_MAG_FILTER,
|
|
glow::LINEAR as i32,
|
|
);
|
|
|
|
gl.tex_image_2d(
|
|
glow::TEXTURE_2D,
|
|
0,
|
|
glow::R8 as i32,
|
|
1024,
|
|
1024,
|
|
0,
|
|
glow::RED,
|
|
glow::UNSIGNED_BYTE,
|
|
None,
|
|
);
|
|
tex
|
|
};
|
|
|
|
Self {
|
|
gl,
|
|
cache: HashMap::new(),
|
|
// cache_tex: vec![0; 1024 * 1024],
|
|
width: 1024,
|
|
height: 1024,
|
|
last_pos: [0, 0],
|
|
tex: RcGlTexture::new(gl, tex),
|
|
}
|
|
}
|
|
|
|
fn get(&self, c: char) -> Option<&TextVType> {
|
|
self.cache.get(&c)
|
|
}
|
|
|
|
fn insert_glyph(&mut self, tex: Image2d<Unorm8>, c: char) -> &TextVType {
|
|
// use image::GrayImage;
|
|
|
|
use ndarray::{s, Array2};
|
|
let width = tex.width();
|
|
let height = tex.height();
|
|
|
|
let data: Array2<u8> = tex.into();
|
|
|
|
let x = self.last_pos[0];
|
|
let y = self.last_pos[1];
|
|
|
|
use glow::PixelUnpackData;
|
|
|
|
unsafe {
|
|
self.gl
|
|
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
|
|
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
|
|
|
|
self.gl.tex_sub_image_2d(
|
|
glow::TEXTURE_2D,
|
|
0,
|
|
x as i32,
|
|
y as i32,
|
|
width as i32,
|
|
height as i32,
|
|
glow::RED,
|
|
glow::UNSIGNED_BYTE,
|
|
PixelUnpackData::Slice(&data.as_slice().unwrap()),
|
|
);
|
|
println!("{} {} {} {}", x, y, width, height);
|
|
println!("size: {}", &data.len());
|
|
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
|
}
|
|
|
|
self.cache.insert(
|
|
c,
|
|
TextVType {
|
|
tex_coords: [
|
|
x as f32,
|
|
(y + height - 1) as f32,
|
|
(x + width - 1) as f32,
|
|
(y + height - 1) as f32,
|
|
(x + width - 1) as f32,
|
|
y as f32,
|
|
x as f32,
|
|
y as f32,
|
|
],
|
|
// tex_coords: [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],
|
|
},
|
|
);
|
|
if x + width >= 1024 {
|
|
self.last_pos[0] = 0;
|
|
self.last_pos[1] += height;
|
|
} else {
|
|
self.last_pos[0] += width;
|
|
}
|
|
|
|
if y + height > self.height {
|
|
self.height += 1024;
|
|
// self.cache_tex.extend(vec![0; 1024 * 1024]);
|
|
}
|
|
|
|
self.cache.get(&c).unwrap()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct TextVType {
|
|
tex_coords: [f32; 8],
|
|
}
|
|
|
|
impl<'a> Text<'a> {
|
|
pub fn new(gl: &'a glow::Context, font_manager: FontManager) -> Result<Self> {
|
|
let vertex = Shader::new(
|
|
glow::VERTEX_SHADER,
|
|
crate::components::CodeType::from_path("font.vert"),
|
|
)?;
|
|
|
|
let fragment = Shader::new(glow::FRAGMENT_SHADER, CodeType::from_path("font.frag"))?;
|
|
|
|
let mut program = Program::new(vertex, fragment, None, "330 core");
|
|
#[cfg(feature = "inparser")]
|
|
program.set_transform(&transform);
|
|
|
|
Ok(Self {
|
|
gl,
|
|
font_manager: RefCell::new(font_manager),
|
|
cache: RefCell::new(HashMap::new()),
|
|
items: Vec::new(),
|
|
program,
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "inparser")]
|
|
pub fn set_viewport(&mut self, viewport: &Viewport) {
|
|
self.program.set_viewport(viewport);
|
|
}
|
|
|
|
fn set_uniforms(&self) {
|
|
let conf = self.program.get_uniform_location(&self.gl, "uSdfConfig");
|
|
let u_mode = self.program.get_uniform_location(&self.gl, "uMode");
|
|
let u_border = self.program.get_uniform_location(&self.gl, "uBorder");
|
|
let u_stroke = self.program.get_uniform_location(&self.gl, "uStroke");
|
|
let u_fill = self.program.get_uniform_location(&self.gl, "uFill");
|
|
|
|
unsafe {
|
|
self.gl.uniform_4_f32(conf.as_ref(), 5.0, 0.0, 0.0, 0.0);
|
|
self.gl.uniform_1_i32(u_mode.as_ref(), -1);
|
|
self.gl.uniform_4_f32(u_border.as_ref(), 0.0, 0.0, 0.0, 0.0);
|
|
self.gl.uniform_4_f32(u_stroke.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
|
self.gl.uniform_4_f32(u_fill.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Graphics for Text<'a> {
|
|
const id: &'static str = "Text";
|
|
type Config = FontConfig;
|
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
|
self.program.compile(gl)
|
|
}
|
|
|
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
|
|
self.program.destroy(gl);
|
|
Ok(())
|
|
}
|
|
|
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
|
|
unsafe {
|
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
|
// gl.polygon_mode(glow::FRONT_AND_BACK, glow::LINE);
|
|
|
|
let loc = self.program.get_uniform_location(gl, "atlas_data");
|
|
gl.uniform_1_i32(loc.as_ref(), 0);
|
|
|
|
gl.active_texture(glow::TEXTURE0);
|
|
gl.bind_texture(
|
|
glow::TEXTURE_2D,
|
|
self.cache
|
|
.borrow()
|
|
.get("resources/Roboto-Regular.ttf")
|
|
.map(|v| v.tex.native()),
|
|
);
|
|
|
|
let width_loc = self.program.get_uniform_location(gl, "atlas_shape");
|
|
gl.uniform_2_f32(width_loc.as_ref(), 1024.0, 1024.0);
|
|
|
|
self.set_uniforms();
|
|
|
|
gl.draw_elements(glow::TRIANGLES, count / 2 * 3, glow::UNSIGNED_INT, 0);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn program_mut(&mut self) -> &mut Program {
|
|
&mut self.program
|
|
}
|
|
|
|
fn program_ref(&self) -> &Program {
|
|
&self.program
|
|
}
|
|
|
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> AttaWithBuffer for Text<'a> {
|
|
type Data = PositionText;
|
|
|
|
fn bake(
|
|
&self,
|
|
data: &Self::Data,
|
|
config: &<Self as Graphics>::Config,
|
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
|
let v = data.bake(
|
|
&self.gl,
|
|
&mut *self.font_manager.borrow_mut(),
|
|
&mut *self.cache.borrow_mut(),
|
|
)?;
|
|
|
|
let mut ebos = Vec::with_capacity(v.len() / 2 * 3);
|
|
|
|
for i in 0..v.len() / 4 {
|
|
let i = i as u32;
|
|
ebos.push(i * 4);
|
|
ebos.push(i * 4 + 1);
|
|
ebos.push(i * 4 + 3);
|
|
|
|
ebos.push(i * 4 + 1);
|
|
ebos.push(i * 4 + 2);
|
|
ebos.push(i * 4 + 3);
|
|
}
|
|
Ok((v.vertex(), Some(ebos), v.len() as i32))
|
|
}
|
|
|
|
fn init(
|
|
&self,
|
|
gl: &glow::Context,
|
|
) -> (
|
|
glow::NativeVertexArray,
|
|
glow::NativeBuffer,
|
|
Option<glow::NativeBuffer>,
|
|
) {
|
|
unsafe {
|
|
let vao = gl.create_vertex_array().unwrap();
|
|
gl.bind_vertex_array(Some(vao));
|
|
let vbo = gl.create_buffer().unwrap();
|
|
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
|
|
gl.enable_vertex_attrib_array(0);
|
|
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 20, 0);
|
|
|
|
gl.enable_vertex_attrib_array(1);
|
|
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, 20, 12);
|
|
|
|
let ebo = gl.create_buffer().unwrap();
|
|
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
|
|
|
|
gl.bind_vertex_array(None);
|
|
|
|
(vao, vbo, Some(ebo))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum FontConfig {
|
|
Textline(LineStyle, FontStyle),
|
|
}
|
|
|
|
mod test {
|
|
#[test]
|
|
fn test() {
|
|
use super::*;
|
|
let mut font_manager = FontManager::new().unwrap();
|
|
}
|
|
}
|