parent
df2543bc8d
commit
b13a834475
@ -0,0 +1,177 @@
|
||||
|
||||
use rusttype::{Font, PositionedGlyph, Scale, Rect, point};
|
||||
use rusttype::gpu_cache::Cache;
|
||||
|
||||
const CACHE_WIDTH: usize = 1000;
|
||||
const CACHE_HEIGHT: usize = 1000;
|
||||
|
||||
/// So currently, I'm using these as container classes which vkprocessor owns
|
||||
/// I then use a CanvasFrame which accumulates lists of handles and vertices.
|
||||
pub struct CanvasText {
|
||||
device: Arc<Device>,
|
||||
queue: Arc<Queue>,
|
||||
font: Font<'static>,
|
||||
cache: Cache<'static>,
|
||||
cache_pixel_buffer: Vec<u8>,
|
||||
texts: Vec<TextData>,
|
||||
}
|
||||
|
||||
impl CanvasText {
|
||||
|
||||
pub fn new() -> CanvasText {
|
||||
|
||||
let cache = Cache::builder().dimensions(CACHE_WIDTH as u32, CACHE_HEIGHT as u32).build();
|
||||
let cache_pixel_buffer = vec!(0; CACHE_WIDTH * CACHE_HEIGHT);
|
||||
|
||||
|
||||
let font_data = include_bytes!("DejaVuSans.ttf");
|
||||
let font = Font::from_bytes(font_data as &[u8]).unwrap();
|
||||
|
||||
CanvasText {
|
||||
device: (),
|
||||
queue: (),
|
||||
font: font,
|
||||
cache: (),
|
||||
cache_pixel_buffer: (),
|
||||
texts: ()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn queue_text(&mut self,
|
||||
x: f32, y: f32,
|
||||
size: f32, color: [f32; 4],
|
||||
text: &str) {
|
||||
let glyphs: Vec<PositionedGlyph> = self.font.layout(text, Scale::uniform(size), point(x, y)).map(|x| x.standalone()).collect();
|
||||
for glyph in &glyphs {
|
||||
self.cache.queue_glyph(0, glyph.clone());
|
||||
}
|
||||
self.texts.push(TextData {
|
||||
glyphs: glyphs.clone(),
|
||||
color: color,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn draw_text(&mut self,
|
||||
command_buffer: AutoCommandBufferBuilder,
|
||||
image_num: usize
|
||||
) -> AutoCommandBufferBuilder {
|
||||
|
||||
let screen_width = 0;
|
||||
let screen_height = 0;
|
||||
|
||||
let cache_pixel_buffer = &mut self.cache_pixel_buffer;
|
||||
let cache = &mut self.cache;
|
||||
|
||||
// update texture cache
|
||||
cache.cache_queued(
|
||||
|rect, src_data| {
|
||||
let width = (rect.max.x - rect.min.x) as usize;
|
||||
let height = (rect.max.y - rect.min.y) as usize;
|
||||
let mut dst_index = rect.min.y as usize * CACHE_WIDTH + rect.min.x as usize;
|
||||
let mut src_index = 0;
|
||||
|
||||
for _ in 0..height {
|
||||
let dst_slice = &mut cache_pixel_buffer[dst_index..dst_index+width];
|
||||
let src_slice = &src_data[src_index..src_index+width];
|
||||
dst_slice.copy_from_slice(src_slice);
|
||||
|
||||
dst_index += CACHE_WIDTH;
|
||||
src_index += width;
|
||||
}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
// need to get a hold of the cache buffer handle after I create it
|
||||
// will then get swapped into this texture buffer
|
||||
// Hmmm so this uninit call returns the texture and then a handle for whatever fills it up
|
||||
let (cache_texture, cache_texture_write) = ImmutableImage::uninitialized(
|
||||
self.device.clone(),
|
||||
Dimensions::Dim2d { width: CACHE_WIDTH as u32, height: CACHE_HEIGHT as u32 },
|
||||
R8Unorm,
|
||||
1,
|
||||
ImageUsage {
|
||||
sampled: true,
|
||||
transfer_destination: true,
|
||||
.. ImageUsage::none()
|
||||
},
|
||||
ImageLayout::General,
|
||||
Some(self.queue.family())
|
||||
).unwrap();
|
||||
|
||||
|
||||
|
||||
let set = Arc::new(
|
||||
PersistentDescriptorSet::start(self.pipeline.clone(), 0)
|
||||
.add_sampled_image(cache_texture.clone(), sampler).unwrap()
|
||||
.build().unwrap()
|
||||
);
|
||||
|
||||
let mut command_buffer = command_buffer
|
||||
.copy_buffer_to_image(
|
||||
buffer.clone(),
|
||||
cache_texture_write,
|
||||
).unwrap()
|
||||
.begin_render_pass(self.framebuffers[image_num].clone(), false, vec!(ClearValue::None)).unwrap();
|
||||
|
||||
// draw
|
||||
for text in &mut self.texts.drain(..) {
|
||||
let vertices: Vec<Vertex> = text.glyphs.iter().flat_map(|g| {
|
||||
if let Ok(Some((uv_rect, screen_rect))) = cache.rect_for(0, g) {
|
||||
let gl_rect = Rect {
|
||||
min: point(
|
||||
(screen_rect.min.x as f32 / screen_width as f32 - 0.5) * 2.0,
|
||||
(screen_rect.min.y as f32 / screen_height as f32 - 0.5) * 2.0
|
||||
),
|
||||
max: point(
|
||||
(screen_rect.max.x as f32 / screen_width as f32 - 0.5) * 2.0,
|
||||
(screen_rect.max.y as f32 / screen_height as f32 - 0.5) * 2.0
|
||||
)
|
||||
};
|
||||
vec!(
|
||||
Vertex {
|
||||
position: [gl_rect.min.x, gl_rect.max.y],
|
||||
tex_position: [uv_rect.min.x, uv_rect.max.y],
|
||||
color: text.color,
|
||||
},
|
||||
Vertex {
|
||||
position: [gl_rect.min.x, gl_rect.min.y],
|
||||
tex_position: [uv_rect.min.x, uv_rect.min.y],
|
||||
color: text.color,
|
||||
},
|
||||
Vertex {
|
||||
position: [gl_rect.max.x, gl_rect.min.y],
|
||||
tex_position: [uv_rect.max.x, uv_rect.min.y],
|
||||
color: text.color,
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: [gl_rect.max.x, gl_rect.min.y],
|
||||
tex_position: [uv_rect.max.x, uv_rect.min.y],
|
||||
color: text.color,
|
||||
},
|
||||
Vertex {
|
||||
position: [gl_rect.max.x, gl_rect.max.y],
|
||||
tex_position: [uv_rect.max.x, uv_rect.max.y],
|
||||
color: text.color,
|
||||
},
|
||||
Vertex {
|
||||
position: [gl_rect.min.x, gl_rect.max.y],
|
||||
tex_position: [uv_rect.min.x, uv_rect.max.y],
|
||||
color: text.color,
|
||||
},
|
||||
).into_iter()
|
||||
}
|
||||
else {
|
||||
vec!().into_iter()
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::all(), vertices.into_iter()).unwrap();
|
||||
command_buffer = command_buffer.draw(self.pipeline.clone(), &DynamicState::none(), vertex_buffer.clone(), set.clone(), ()).unwrap();
|
||||
}
|
||||
|
||||
command_buffer.end_render_pass().unwrap()
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in new issue