Refactor: create Texture struct
This commit is contained in:
parent
7f83876005
commit
616245c323
7 changed files with 102 additions and 87 deletions
2
TODO.md
2
TODO.md
|
@ -1,6 +1,6 @@
|
||||||
# Code
|
# Code
|
||||||
* Break off example app from emigui_wasm
|
* Break off example app from emigui_wasm
|
||||||
* Add Rect type to GuiCmd. Every thing has a bounding rectangle, even text
|
* Try it on iPhone
|
||||||
* Dynamic fonts:
|
* Dynamic fonts:
|
||||||
* Font options (sizes)
|
* Font options (sizes)
|
||||||
* Read ttf via web?
|
* Read ttf via web?
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
style,
|
style,
|
||||||
types::GuiInput,
|
types::GuiInput,
|
||||||
widgets::*,
|
widgets::*,
|
||||||
Frame, RawInput,
|
Frame, RawInput, Texture,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
@ -55,7 +55,7 @@ impl Emigui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> (u16, u16, &[u8]) {
|
pub fn texture(&self) -> &Texture {
|
||||||
self.data.fonts.texture()
|
self.data.fonts.texture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,11 +98,12 @@ impl Font {
|
||||||
|
|
||||||
let glyph_pos = atlas_lock.allocate((glyph_width, glyph_height));
|
let glyph_pos = atlas_lock.allocate((glyph_width, glyph_height));
|
||||||
|
|
||||||
|
let texture = atlas_lock.texture_mut();
|
||||||
glyph.draw(|x, y, v| {
|
glyph.draw(|x, y, v| {
|
||||||
if v > 0.0 {
|
if v > 0.0 {
|
||||||
let px = glyph_pos.0 + x as usize;
|
let px = glyph_pos.0 + x as usize;
|
||||||
let py = glyph_pos.1 + y as usize;
|
let py = glyph_pos.1 + y as usize;
|
||||||
atlas_lock[(px, py)] = (v * 255.0).round() as u8;
|
texture[(px, py)] = (v * 255.0).round() as u8;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -271,7 +272,8 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_print_all_chars(&self) {
|
pub fn debug_print_all_chars(&self) {
|
||||||
let atlas_lock = self.atlas.lock().unwrap();
|
let mut atlas_lock = self.atlas.lock().unwrap();
|
||||||
|
let texture_mut = atlas_lock.texture_mut();
|
||||||
|
|
||||||
let max_width = 160;
|
let max_width = 160;
|
||||||
let scale = Scale::uniform(self.scale as f32);
|
let scale = Scale::uniform(self.scale as f32);
|
||||||
|
@ -295,7 +297,7 @@ impl Font {
|
||||||
if let Some(uv) = glyph.uv {
|
if let Some(uv) = glyph.uv {
|
||||||
for x in uv.min.0..=uv.max.0 {
|
for x in uv.min.0..=uv.max.0 {
|
||||||
for y in uv.min.1..=uv.max.1 {
|
for y in uv.min.1..=uv.max.1 {
|
||||||
let pixel = atlas_lock[(x as usize, y as usize)];
|
let pixel = texture_mut[(x as usize, y as usize)];
|
||||||
let rx = uv.offset.0 + x as i16 - uv.min.0 as i16;
|
let rx = uv.offset.0 + x as i16 - uv.min.0 as i16;
|
||||||
let ry = uv.offset.1 + y as i16 - uv.min.1 as i16;
|
let ry = uv.offset.1 + y as i16 - uv.min.1 as i16;
|
||||||
let px = (cursor_x + rx as f32).round();
|
let px = (cursor_x + rx as f32).round();
|
||||||
|
|
|
@ -3,7 +3,10 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{font::Font, texture_atlas::TextureAtlas};
|
use crate::{
|
||||||
|
font::Font,
|
||||||
|
texture_atlas::{Texture, TextureAtlas},
|
||||||
|
};
|
||||||
|
|
||||||
/// TODO: rename
|
/// TODO: rename
|
||||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
@ -16,7 +19,7 @@ pub enum TextStyle {
|
||||||
|
|
||||||
pub struct Fonts {
|
pub struct Fonts {
|
||||||
fonts: BTreeMap<TextStyle, Font>,
|
fonts: BTreeMap<TextStyle, Font>,
|
||||||
texture: (u16, u16, Vec<u8>),
|
texture: Texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fonts {
|
impl Fonts {
|
||||||
|
@ -25,27 +28,30 @@ impl Fonts {
|
||||||
|
|
||||||
// Make one white pixel for use for various stuff:
|
// Make one white pixel for use for various stuff:
|
||||||
let pos = atlas.allocate((1, 1));
|
let pos = atlas.allocate((1, 1));
|
||||||
atlas[pos] = 255;
|
atlas.texture_mut()[pos] = 255;
|
||||||
|
|
||||||
let atlas = Arc::new(Mutex::new(atlas));
|
let atlas = Arc::new(Mutex::new(atlas));
|
||||||
|
|
||||||
// TODO: figure out a way to make the wasm smaller despite including a font.
|
// TODO: figure out a way to make the wasm smaller despite including a font.
|
||||||
// let font_data = include_bytes!("../fonts/ProggyClean.ttf"); // Use 13 for this. NOTHING ELSE.
|
// let typeface_data = include_bytes!("../fonts/ProggyClean.ttf"); // Use 13 for this. NOTHING ELSE.
|
||||||
// let font_data = include_bytes!("../fonts/DejaVuSans.ttf");
|
// let typeface_data = include_bytes!("../fonts/DejaVuSans.ttf");
|
||||||
let font_data = include_bytes!("../fonts/Roboto-Regular.ttf");
|
let typeface_data = include_bytes!("../fonts/Roboto-Regular.ttf");
|
||||||
|
|
||||||
let mut fonts = BTreeMap::new();
|
let mut fonts = BTreeMap::new();
|
||||||
fonts.insert(TextStyle::Body, Font::new(atlas.clone(), font_data, 20));
|
fonts.insert(TextStyle::Body, Font::new(atlas.clone(), typeface_data, 20));
|
||||||
fonts.insert(TextStyle::Button, fonts[&TextStyle::Body].clone());
|
fonts.insert(TextStyle::Button, fonts[&TextStyle::Body].clone());
|
||||||
fonts.insert(TextStyle::Heading, Font::new(atlas.clone(), font_data, 30));
|
fonts.insert(
|
||||||
|
TextStyle::Heading,
|
||||||
|
Font::new(atlas.clone(), typeface_data, 30),
|
||||||
|
);
|
||||||
|
|
||||||
let texture = atlas.lock().unwrap().clone().into_texture();
|
let texture = atlas.lock().unwrap().clone().texture().clone();
|
||||||
|
|
||||||
Fonts { fonts, texture }
|
Fonts { fonts, texture }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> (u16, u16, &[u8]) {
|
pub fn texture(&self) -> &Texture {
|
||||||
(self.texture.0, self.texture.1, &self.texture.2)
|
&self.texture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,6 @@ pub use crate::{
|
||||||
layout::{Align, LayoutOptions, Region},
|
layout::{Align, LayoutOptions, Region},
|
||||||
mesher::{Frame, Vertex},
|
mesher::{Frame, Vertex},
|
||||||
style::Style,
|
style::Style,
|
||||||
|
texture_atlas::Texture,
|
||||||
types::RawInput,
|
types::RawInput,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,63 +1,13 @@
|
||||||
/// A texture pixels, used for fonts.
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct TextureAtlas {
|
pub struct Texture {
|
||||||
width: usize,
|
/// e.g. a hash of the data. Use this to detect changes!
|
||||||
height: usize,
|
pub id: u64, // TODO
|
||||||
pixels: Vec<u8>,
|
pub width: usize,
|
||||||
|
pub height: usize,
|
||||||
/// Used for when adding new rects
|
pub pixels: Vec<u8>,
|
||||||
cursor: (usize, usize),
|
|
||||||
row_height: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureAtlas {
|
impl std::ops::Index<(usize, usize)> for Texture {
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
|
||||||
TextureAtlas {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
pixels: vec![0; width * height],
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn width(&self) -> usize {
|
|
||||||
self.width
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height(&self) -> usize {
|
|
||||||
self.height
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_texture(self) -> (u16, u16, Vec<u8>) {
|
|
||||||
(self.width as u16, self.height as u16, self.pixels)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the coordinates of where the rect ended up.
|
|
||||||
pub fn allocate(&mut self, (w, h): (usize, usize)) -> (usize, usize) {
|
|
||||||
assert!(w <= self.width);
|
|
||||||
if self.cursor.0 + w > self.width {
|
|
||||||
// New row:
|
|
||||||
self.cursor.0 = 0;
|
|
||||||
self.cursor.1 += self.row_height;
|
|
||||||
self.row_height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.row_height = self.row_height.max(h);
|
|
||||||
while self.cursor.1 + self.row_height >= self.height {
|
|
||||||
self.height *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.width * self.height > self.pixels.len() {
|
|
||||||
self.pixels.resize(self.width * self.height, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pos = self.cursor;
|
|
||||||
self.cursor.0 += w;
|
|
||||||
(pos.0 as usize, pos.1 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Index<(usize, usize)> for TextureAtlas {
|
|
||||||
type Output = u8;
|
type Output = u8;
|
||||||
|
|
||||||
fn index(&self, (x, y): (usize, usize)) -> &u8 {
|
fn index(&self, (x, y): (usize, usize)) -> &u8 {
|
||||||
|
@ -67,10 +17,69 @@ impl std::ops::Index<(usize, usize)> for TextureAtlas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::IndexMut<(usize, usize)> for TextureAtlas {
|
impl std::ops::IndexMut<(usize, usize)> for Texture {
|
||||||
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut u8 {
|
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut u8 {
|
||||||
assert!(x < self.width);
|
assert!(x < self.width);
|
||||||
assert!(y < self.height);
|
assert!(y < self.height);
|
||||||
&mut self.pixels[y * self.width + x]
|
&mut self.pixels[y * self.width + x]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A texture pixels, used for fonts.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct TextureAtlas {
|
||||||
|
texture: Texture,
|
||||||
|
|
||||||
|
/// Used for when adding new rects
|
||||||
|
cursor: (usize, usize),
|
||||||
|
row_height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureAtlas {
|
||||||
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
|
TextureAtlas {
|
||||||
|
texture: Texture {
|
||||||
|
id: 0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pixels: vec![0; width * height],
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texture(&self) -> &Texture {
|
||||||
|
&self.texture
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texture_mut(&mut self) -> &mut Texture {
|
||||||
|
&mut self.texture
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the coordinates of where the rect ended up.
|
||||||
|
pub fn allocate(&mut self, (w, h): (usize, usize)) -> (usize, usize) {
|
||||||
|
assert!(w <= self.texture.width);
|
||||||
|
if self.cursor.0 + w > self.texture.width {
|
||||||
|
// New row:
|
||||||
|
self.cursor.0 = 0;
|
||||||
|
self.cursor.1 += self.row_height;
|
||||||
|
self.row_height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.row_height = self.row_height.max(h);
|
||||||
|
while self.cursor.1 + self.row_height >= self.texture.height {
|
||||||
|
self.texture.height *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.texture.width * self.texture.height > self.texture.pixels.len() {
|
||||||
|
self.texture
|
||||||
|
.pixels
|
||||||
|
.resize(self.texture.width * self.texture.height, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = self.cursor;
|
||||||
|
self.cursor.0 += w;
|
||||||
|
self.texture.id += 1; // TODO: hash this ?
|
||||||
|
(pos.0 as usize, pos.1 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use {
|
||||||
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader, WebGlTexture},
|
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader, WebGlTexture},
|
||||||
};
|
};
|
||||||
|
|
||||||
use emigui::Frame;
|
use emigui::{Frame, Texture};
|
||||||
|
|
||||||
type Gl = WebGlRenderingContext;
|
type Gl = WebGlRenderingContext;
|
||||||
|
|
||||||
|
@ -32,10 +32,7 @@ impl Painter {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(canvas_id: &str, texture: &Texture) -> Result<Painter, JsValue> {
|
||||||
canvas_id: &str,
|
|
||||||
(tex_width, tex_height, pixels): (u16, u16, &[u8]),
|
|
||||||
) -> Result<Painter, JsValue> {
|
|
||||||
let document = web_sys::window().unwrap().document().unwrap();
|
let document = web_sys::window().unwrap().document().unwrap();
|
||||||
let canvas = document.get_element_by_id(canvas_id).unwrap();
|
let canvas = document.get_element_by_id(canvas_id).unwrap();
|
||||||
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||||
|
@ -47,11 +44,11 @@ impl Painter {
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
let texture = gl.create_texture().unwrap();
|
let gl_texture = gl.create_texture().unwrap();
|
||||||
gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
gl.bind_texture(Gl::TEXTURE_2D, Some(&gl_texture));
|
||||||
|
|
||||||
// TODO: remove once https://github.com/rustwasm/wasm-bindgen/issues/1005 is fixed.
|
// TODO: remove once https://github.com/rustwasm/wasm-bindgen/issues/1005 is fixed.
|
||||||
let mut pixels: Vec<_> = pixels.iter().cloned().collect();
|
let mut pixels: Vec<_> = texture.pixels.iter().cloned().collect();
|
||||||
|
|
||||||
let level = 0;
|
let level = 0;
|
||||||
let internal_format = Gl::ALPHA;
|
let internal_format = Gl::ALPHA;
|
||||||
|
@ -62,8 +59,8 @@ impl Painter {
|
||||||
Gl::TEXTURE_2D,
|
Gl::TEXTURE_2D,
|
||||||
level,
|
level,
|
||||||
internal_format as i32,
|
internal_format as i32,
|
||||||
tex_width as i32,
|
texture.width as i32,
|
||||||
tex_height as i32,
|
texture.height as i32,
|
||||||
border,
|
border,
|
||||||
src_format,
|
src_format,
|
||||||
src_type,
|
src_type,
|
||||||
|
@ -122,13 +119,13 @@ impl Painter {
|
||||||
Ok(Painter {
|
Ok(Painter {
|
||||||
canvas,
|
canvas,
|
||||||
gl,
|
gl,
|
||||||
texture,
|
texture: gl_texture,
|
||||||
program,
|
program,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
pos_buffer,
|
pos_buffer,
|
||||||
tc_buffer,
|
tc_buffer,
|
||||||
color_buffer,
|
color_buffer,
|
||||||
tex_size: (tex_width, tex_height),
|
tex_size: (texture.width as u16, texture.height as u16),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue