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, cache: RefCell>>, items: Vec, program: Program, } pub struct Cache<'a> { gl: &'a glow::Context, cache: HashMap, // cache_tex: Vec, 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, c: char) -> &TextVType { // use image::GrayImage; use ndarray::{s, Array2}; let width = tex.width(); let height = tex.height(); let data: Array2 = 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 { 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: &::Config, ) -> Result<(Vec, Option>, 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, ) { 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(); } }