radar-gi/src/graphics/font/mod.rs
2024-08-16 16:30:06 +08:00

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();
}
}