#[cfg(feature="rusttype")] extern crate rusttype; use std::alloc::{self, Layout}; use std::{cmp, ptr}; use std::ptr::NonNull; #[cfg(feature="rusttype")] use self::rusttype::{Font, FontCollection, Scale, point}; #[cfg(not(feature="rusttype"))] use orbclient::FONT; #[cfg(feature="rusttype")] static FONT: &'static [u8] = include_bytes!("../res/DejaVuSansMono.ttf"); #[cfg(feature="rusttype")] static FONT_BOLD: &'static [u8] = include_bytes!("../res/DejaVuSansMono-Bold.ttf"); #[cfg(feature="rusttype")] static FONT_BOLD_ITALIC: &'static [u8] = include_bytes!("../res/DejaVuSansMono-BoldOblique.ttf"); #[cfg(feature="rusttype")] static FONT_ITALIC: &'static [u8] = include_bytes!("../res/DejaVuSansMono-Oblique.ttf"); pub struct OffscreenBuffer { ptr: NonNull<[u32]>, } impl OffscreenBuffer { #[inline] fn layout(len: usize) -> Layout { // optimizes to an integer mul Layout::array::(len).unwrap().align_to(4096).unwrap() } #[inline] fn new(len: usize) -> Self { let layout = Self::layout(len); let ptr = unsafe { alloc::alloc_zeroed(layout) }; let ptr = ptr::slice_from_raw_parts_mut(ptr.cast(), len); let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout)); OffscreenBuffer { ptr } } } impl Drop for OffscreenBuffer { fn drop(&mut self) { let layout = Self::layout(self.ptr.len()); unsafe { alloc::dealloc(self.ptr.as_ptr().cast(), layout) }; } } impl std::ops::Deref for OffscreenBuffer { type Target = [u32]; fn deref(&self) -> &[u32] { unsafe { self.ptr.as_ref() } } } impl std::ops::DerefMut for OffscreenBuffer { fn deref_mut(&mut self) -> &mut [u32] { unsafe { self.ptr.as_mut() } } } /// A display pub struct Display { pub width: usize, pub height: usize, pub offscreen: OffscreenBuffer, #[cfg(feature="rusttype")] pub font: Font<'static>, #[cfg(feature="rusttype")] pub font_bold: Font<'static>, #[cfg(feature="rusttype")] pub font_bold_italic: Font<'static>, #[cfg(feature="rusttype")] pub font_italic: Font<'static> } impl Display { #[cfg(not(feature="rusttype"))] pub fn new(width: usize, height: usize) -> Display { let size = width * height; let offscreen = OffscreenBuffer::new(size); Display { width, height, offscreen, } } #[cfg(feature="rusttype")] pub fn new(width: usize, height: usize) -> Display { let size = width * height; let offscreen = OffscreenBuffer::new(size); Display { width, height, offscreen, font: FontCollection::from_bytes(FONT).into_font().unwrap(), font_bold: FontCollection::from_bytes(FONT_BOLD).into_font().unwrap(), font_bold_italic: FontCollection::from_bytes(FONT_BOLD_ITALIC).into_font().unwrap(), font_italic: FontCollection::from_bytes(FONT_ITALIC).into_font().unwrap() } } pub fn resize(&mut self, width: usize, height: usize) { if width != self.width || height != self.height { println!("Resize display to {}, {}", width, height); let size = width * height; let mut offscreen = OffscreenBuffer::new(size); { let mut old_ptr = self.offscreen.as_ptr(); let mut new_ptr = offscreen.as_mut_ptr(); for _y in 0..cmp::min(height, self.height) { unsafe { ptr::copy( old_ptr as *const u8, new_ptr as *mut u8, cmp::min(width, self.width) * 4 ); if width > self.width { ptr::write_bytes( new_ptr.offset(self.width as isize), 0, width - self.width ); } old_ptr = old_ptr.offset(self.width as isize); new_ptr = new_ptr.offset(width as isize); } } if height > self.height { for _y in self.height..height { unsafe { ptr::write_bytes(new_ptr, 0, width); new_ptr = new_ptr.offset(width as isize); } } } } self.width = width; self.height = height; self.offscreen = offscreen; } else { println!("Display is already {}, {}", width, height); } } /// Draw a rectangle pub fn rect(&mut self, x: usize, y: usize, w: usize, h: usize, color: u32) { let start_y = cmp::min(self.height, y); let end_y = cmp::min(self.height, y + h); let start_x = cmp::min(self.width, x); let len = cmp::min(self.width, x + w) - start_x; let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; let stride = self.width * 4; let offset = y * stride + start_x * 4; offscreen_ptr += offset; let mut rows = end_y - start_y; while rows > 0 { for i in 0..len { unsafe { *(offscreen_ptr as *mut u32).add(i) = color; } } offscreen_ptr += stride; rows -= 1; } } /// Invert a rectangle pub fn invert(&mut self, x: usize, y: usize, w: usize, h: usize) { let start_y = cmp::min(self.height, y); let end_y = cmp::min(self.height, y + h); let start_x = cmp::min(self.width, x); let len = cmp::min(self.width, x + w) - start_x; let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; let stride = self.width * 4; let offset = y * stride + start_x * 4; offscreen_ptr += offset; let mut rows = end_y - start_y; while rows > 0 { let mut row_ptr = offscreen_ptr; let mut cols = len; while cols > 0 { unsafe { let color = *(row_ptr as *mut u32); *(row_ptr as *mut u32) = !color; } row_ptr += 4; cols -= 1; } offscreen_ptr += stride; rows -= 1; } } /// Draw a character #[cfg(not(feature="rusttype"))] pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, _bold: bool, _italic: bool) { if x + 8 <= self.width && y + 16 <= self.height { let mut dst = self.offscreen.as_mut_ptr() as usize + (y * self.width + x) * 4; let font_i = 16 * (character as usize); if font_i + 16 <= FONT.len() { for row in 0..16 { let row_data = FONT[font_i + row]; for col in 0..8 { if (row_data >> (7 - col)) & 1 == 1 { unsafe { *((dst + col * 4) as *mut u32) = color; } } } dst += self.width * 4; } } } } /// Draw a character #[cfg(feature="rusttype")] pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, bold: bool, italic: bool) { let width = self.width; let height = self.height; let offscreen = self.offscreen.as_mut_ptr() as usize; let font = if bold && italic { &self.font_bold_italic } else if bold { &self.font_bold } else if italic { &self.font_italic } else { &self.font }; if let Some(glyph) = font.glyph(character){ let scale = Scale::uniform(16.0); let v_metrics = font.v_metrics(scale); let point = point(0.0, v_metrics.ascent); let glyph = glyph.scaled(scale).positioned(point); if let Some(bb) = glyph.pixel_bounding_box() { glyph.draw(|off_x, off_y, v| { let off_x = x + (off_x as i32 + bb.min.x) as usize; let off_y = y + (off_y as i32 + bb.min.y) as usize; // There's still a possibility that the glyph clips the boundaries of the bitmap if off_x < width && off_y < height { if v > 0.0 { let f_a = (v * 255.0) as u32; let f_r = (((color >> 16) & 0xFF) * f_a)/255; let f_g = (((color >> 8) & 0xFF) * f_a)/255; let f_b = ((color & 0xFF) * f_a)/255; let offscreen_ptr = (offscreen + (off_y * width + off_x) * 4) as *mut u32; let bg = unsafe { *offscreen_ptr }; let b_a = 255 - f_a; let b_r = (((bg >> 16) & 0xFF) * b_a)/255; let b_g = (((bg >> 8) & 0xFF) * b_a)/255; let b_b = ((bg & 0xFF) * b_a)/255; let c = ((f_r + b_r) << 16) | ((f_g + b_g) << 8) | (f_b + b_b); unsafe { *offscreen_ptr = c; } } } }); } } } /// Copy from offscreen to onscreen pub fn sync(&mut self, x: usize, y: usize, w: usize, h: usize, onscreen: &mut [u32], stride: usize) { let start_y = cmp::min(self.height, y); let end_y = cmp::min(self.height, y + h); let start_x = cmp::min(self.width, x); let len = (cmp::min(self.width, x + w) - start_x) * 4; let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; let mut onscreen_ptr = onscreen.as_mut_ptr() as usize; offscreen_ptr += (y * self.width + start_x) * 4; onscreen_ptr += (y * stride + start_x) * 4; let mut rows = end_y - start_y; while rows > 0 { unsafe { ptr::copy( offscreen_ptr as *const u8, onscreen_ptr as *mut u8, len ); } offscreen_ptr += self.width * 4; onscreen_ptr += stride * 4; rows -= 1; } } }