Refactor: create Texture struct

This commit is contained in:
Emil Ernerfeldt 2019-01-16 17:09:12 -06:00
parent 7f83876005
commit 616245c323
7 changed files with 102 additions and 87 deletions

View file

@ -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?

View file

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

View file

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

View file

@ -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
} }
} }

View file

@ -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,
}; };

View file

@ -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)
}
}

View file

@ -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),
}) })
} }