diff --git a/Cargo.toml b/Cargo.toml index 4514f560..0fdc806c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ memmap = "0.5" path = "/Users/pcwalton/Source/rust-packages/compute-shader" [dev-dependencies] +image = "0.12" quickcheck = "0.4" [dev-dependencies.glfw] diff --git a/examples/lorem-ipsum.rs b/examples/lorem-ipsum.rs index 3b122c76..b59076c5 100644 --- a/examples/lorem-ipsum.rs +++ b/examples/lorem-ipsum.rs @@ -8,6 +8,7 @@ extern crate compute_shader; extern crate euclid; extern crate gl; extern crate glfw; +extern crate image; extern crate memmap; extern crate pathfinder; @@ -29,6 +30,7 @@ use pathfinder::shaper; use std::env; use std::mem; use std::os::raw::c_void; +use std::path::Path; const ATLAS_SIZE: u32 = 2048; const WIDTH: u32 = 640; @@ -47,6 +49,8 @@ static FPS_BACKGROUND_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 0.7]; static FPS_FOREGROUND_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; static TEXT_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0]; +static ATLAS_DUMP_FILENAME: &'static str = "lorem-ipsum-atlas.png"; + fn main() { let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); glfw.window_hint(WindowHint::ContextVersion(3, 3)); @@ -56,6 +60,7 @@ fn main() { let (mut window, events) = context.expect("Couldn't create a window!"); window.make_current(); + window.set_key_polling(true); window.set_scroll_polling(true); window.set_size_polling(true); window.set_framebuffer_size_polling(true); @@ -155,6 +160,10 @@ fn main() { WindowEvent::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true) } + WindowEvent::Key(Key::S, _, Action::Press, _) => { + renderer.take_screenshot(); + println!("wrote screenshot to: {}", ATLAS_DUMP_FILENAME); + } WindowEvent::Scroll(x, y) => { if window.get_key(Key::LeftAlt) == Action::Press || window.get_key(Key::RightAlt) == Action::Press { @@ -611,6 +620,36 @@ impl Renderer { FPS_DISPLAY_POINT_SIZE, &FPS_FOREGROUND_COLOR); } + + fn take_screenshot(&self) { + unsafe { + let mut fbo = 0; + gl::GenFramebuffers(1, &mut fbo); + gl::BindFramebuffer(gl::FRAMEBUFFER, fbo); + gl::FramebufferTexture2D(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_RECTANGLE, + self.main_gl_texture, + 0); + + let length = 4 * self.atlas_size.width as usize * self.atlas_size.height as usize; + let mut pixels: Vec = vec![0; length]; + gl::ReadPixels(0, 0, + self.atlas_size.width as GLint, self.atlas_size.height as GLint, + gl::RGBA, + gl::UNSIGNED_BYTE, + pixels.as_mut_ptr() as *mut c_void); + + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::DeleteFramebuffers(1, &mut fbo); + + image::save_buffer(&Path::new(ATLAS_DUMP_FILENAME), + &pixels, + self.atlas_size.width, + self.atlas_size.height, + image::RGBA(8)).unwrap(); + } + } } #[derive(Clone, Copy, Debug)] diff --git a/src/atlas.rs b/src/atlas.rs index 11d7628c..aad54d5a 100644 --- a/src/atlas.rs +++ b/src/atlas.rs @@ -32,12 +32,15 @@ impl Atlas { } pub fn place(&mut self, size: &Size2D) -> Result, ()> { + // Add a one-pixel border to prevent bleed. + let alloc_size = *size + Size2D::new(2, 2); + let chosen_index_and_rect = self.free_rects .iter() .enumerate() .filter(|&(_, rect)| { - size.width <= rect.size.width && size.height <= rect.size.height + alloc_size.width <= rect.size.width && alloc_size.height <= rect.size.height }) .min_by(|&(_, a), &(_, b)| area(a).cmp(&area(b))) .map(|(index, rect)| (index, *rect)); @@ -59,16 +62,17 @@ impl Atlas { // Guillotine to bottom. let free_below = - Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + size.height), - Size2D::new(size.width, chosen_rect.size.height - size.height)); + Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + alloc_size.height), + Size2D::new(alloc_size.width, chosen_rect.size.height - alloc_size.height)); if !free_below.is_empty() { self.free_rects.push(free_below); } // Guillotine to right. let free_to_right = - Rect::new(Point2D::new(chosen_rect.origin.x + size.width, chosen_rect.origin.y), - Size2D::new(chosen_rect.size.width - size.width, chosen_rect.size.height)); + Rect::new(Point2D::new(chosen_rect.origin.x + alloc_size.width, chosen_rect.origin.y), + Size2D::new(chosen_rect.size.width - alloc_size.width, + chosen_rect.size.height)); if !free_to_right.is_empty() { self.free_rects.push(free_to_right); } @@ -79,7 +83,8 @@ impl Atlas { self.width_of_last_shelf = chosen_rect.max_x() } - Ok(chosen_rect.origin) + let object_origin = chosen_rect.origin + Point2D::new(1, 1); + Ok(object_origin) } #[inline]