use crate::{AllocId, Allocation, AllocatorOptions, DEFAULT_OPTIONS, Size, Rectangle, point2, size2}; const SHELF_SPLIT_THRESHOLD: u16 = 8; const ITEM_SPLIT_THRESHOLD: u16 = 8; #[repr(transparent)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] struct ShelfIndex(u16); impl ShelfIndex { const NONE: Self = ShelfIndex(std::u16::MAX); fn index(self) -> usize { self.0 as usize } fn is_some(self) -> bool { self.0 != std::u16::MAX } fn is_none(self) -> bool { self.0 == std::u16::MAX } } #[repr(transparent)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] struct ItemIndex(u16); impl ItemIndex { const NONE: Self = ItemIndex(std::u16::MAX); fn index(self) -> usize { self.0 as usize } fn is_some(self) -> bool { self.0 != std::u16::MAX } fn is_none(self) -> bool { self.0 == std::u16::MAX } } #[derive(Clone)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] struct Shelf { x: u16, y: u16, height: u16, prev: ShelfIndex, next: ShelfIndex, first_item: ItemIndex, is_empty: bool, } #[derive(Clone)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] struct Item { x: u16, width: u16, prev: ItemIndex, next: ItemIndex, shelf: ShelfIndex, allocated: bool, generation: u16, } // Note: if allocating is slow we can use the guillotiere trick of storing multiple lists of free // rects (per shelf height) instead of iterating the shelves and items. /// A shelf-packing dynamic texture atlas allocator tracking each allocation individually and with support /// for coalescing empty shelves. #[derive(Clone)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct AtlasAllocator { shelves: Vec, items: Vec, alignment: Size, flip_xy: bool, size: Size, first_shelf: ShelfIndex, free_items: ItemIndex, free_shelves: ShelfIndex, shelf_width: u16, allocated_space: i32, } impl AtlasAllocator { /// Create an atlas allocator with provided options. pub fn with_options(size: Size, options: &AllocatorOptions) -> Self { let (shelf_alignment, width, height) = if options.vertical_shelves { (options.alignment.height, size.height, size.width) } else { (options.alignment.width, size.width, size.height) }; let mut shelf_width = width / options.num_columns; shelf_width -= shelf_width % shelf_alignment; let mut atlas = AtlasAllocator { shelves: Vec::new(), items: Vec::new(), size: size2(width, height), alignment: options.alignment, flip_xy: options.vertical_shelves, first_shelf: ShelfIndex(0), free_items: ItemIndex::NONE, free_shelves: ShelfIndex::NONE, shelf_width: shelf_width as u16, allocated_space: 0, }; atlas.init(); atlas } /// Create an atlas allocator with default options. pub fn new(size: Size) -> Self { Self::with_options(size, &DEFAULT_OPTIONS) } pub fn clear(&mut self) { self.init(); } fn init(&mut self) { assert!(self.size.width > 0); assert!(self.size.height > 0); assert!(self.size.width <= std::u16::MAX as i32); assert!(self.size.height <= std::u16::MAX as i32); assert!( self.size.width.checked_mul(self.size.height).is_some(), "The area of the atlas must fit in a i32 value" ); assert!(self.alignment.width > 0); assert!(self.alignment.height > 0); self.shelves.clear(); self.items.clear(); let num_columns = self.size.width as u16 / self.shelf_width; let mut prev = ShelfIndex::NONE; for i in 0..num_columns { let first_item = ItemIndex(self.items.len() as u16); let x = i * self.shelf_width; let current = ShelfIndex(i); let next = if i + 1 < num_columns { ShelfIndex(i + 1) } else { ShelfIndex::NONE }; self.shelves.push(Shelf { x, y: 0, height: self.size.height as u16, prev, next, is_empty: true, first_item, }); self.items.push(Item { x, width: self.shelf_width, prev: ItemIndex::NONE, next: ItemIndex::NONE, shelf: current, allocated: false, generation: 1, }); prev = current; } self.first_shelf = ShelfIndex(0); self.free_items = ItemIndex::NONE; self.free_shelves = ShelfIndex::NONE; self.allocated_space = 0; } pub fn size(&self) -> Size { if self.flip_xy { size2(self.size.height, self.size.width) } else { self.size } } /// Allocate a rectangle in the atlas. pub fn allocate(&mut self, mut size: Size) -> Option { if size.is_empty() || size.width > std::u16::MAX as i32 || size.height > std::u16::MAX as i32 { return None; } adjust_size(self.alignment.width, &mut size.width); adjust_size(self.alignment.height, &mut size.height); let (width, height) = convert_coordinates(self.flip_xy, size.width, size.height); if width > self.shelf_width as i32 || height > self.size.height { return None; } let height = shelf_height(height, self.size.height); let mut width = width as u16; let mut height = height as u16; let mut selected_shelf_height = std::u16::MAX; let mut selected_shelf = ShelfIndex::NONE; let mut selected_item = ItemIndex::NONE; let mut shelf_idx = self.first_shelf; while shelf_idx.is_some() { let shelf = &self.shelves[shelf_idx.index()]; if shelf.height < height || shelf.height >= selected_shelf_height || (!shelf.is_empty && shelf.height > height + height / 2) { shelf_idx = shelf.next; continue; } let mut item_idx = shelf.first_item; while item_idx.is_some() { let item = &self.items[item_idx.index()]; if !item.allocated && item.width >= width { break; } item_idx = item.next; } if item_idx.is_some() { selected_shelf = shelf_idx; selected_shelf_height = shelf.height; selected_item = item_idx; if shelf.height == height { // Perfect fit, stop searching. break; } } shelf_idx = shelf.next; } if selected_shelf.is_none() { return None; } let shelf = self.shelves[selected_shelf.index()].clone(); if shelf.is_empty { self.shelves[selected_shelf.index()].is_empty = false; } if shelf.is_empty && shelf.height > height + SHELF_SPLIT_THRESHOLD { // Split the empty shelf into one of the desired size and a new // empty one with a single empty item. let new_shelf_idx = self.add_shelf(Shelf { x: shelf.x, y: shelf.y + height, height: shelf.height - height, prev: selected_shelf, next: shelf.next, first_item: ItemIndex::NONE, is_empty: true, }); let new_item_idx = self.add_item(Item { x: shelf.x, width: self.shelf_width, prev: ItemIndex::NONE, next: ItemIndex::NONE, shelf: new_shelf_idx, allocated: false, generation: 1, }); self.shelves[new_shelf_idx.index()].first_item = new_item_idx; let next = self.shelves[selected_shelf.index()].next; self.shelves[selected_shelf.index()].height = height; self.shelves[selected_shelf.index()].next = new_shelf_idx; if next.is_some() { self.shelves[next.index()].prev = new_shelf_idx; } } else { height = shelf.height; } let item = self.items[selected_item.index()].clone(); if item.width - width > ITEM_SPLIT_THRESHOLD { let new_item_idx = self.add_item(Item { x: item.x + width, width: item.width - width, prev: selected_item, next: item.next, shelf: item.shelf, allocated: false, generation: 1, }); self.items[selected_item.index()].width = width; self.items[selected_item.index()].next = new_item_idx; if item.next.is_some() { self.items[item.next.index()].prev = new_item_idx; } } else { width = item.width; } self.items[selected_item.index()].allocated = true; let generation = self.items[selected_item.index()].generation; let x0 = item.x; let y0 = shelf.y; let x1 = x0 + width; let y1 = y0 + height; let (x0, y0) = convert_coordinates(self.flip_xy, x0 as i32, y0 as i32); let (x1, y1) = convert_coordinates(self.flip_xy, x1 as i32, y1 as i32); self.check(); let rectangle = Rectangle { min: point2(x0, y0), max: point2(x1, y1), }; self.allocated_space += rectangle.area(); Some(Allocation { id: AllocId::new(selected_item.0, generation), rectangle, }) } /// Deallocate a rectangle in the atlas. pub fn deallocate(&mut self, id: AllocId) { let item_idx = ItemIndex(id.index()); //let item = self.items[item_idx.index()].clone(); let Item { mut prev, mut next, mut width, allocated, shelf, generation, .. } = self.items[item_idx.index()]; assert!(allocated); assert_eq!(generation, id.generation(), "Invalid AllocId"); self.items[item_idx.index()].allocated = false; self.allocated_space -= width as i32 * self.shelves[shelf.index()].height as i32; if next.is_some() && !self.items[next.index()].allocated { // Merge the next item into this one. let next_next = self.items[next.index()].next; let next_width = self.items[next.index()].width; self.items[item_idx.index()].next = next_next; self.items[item_idx.index()].width += next_width; width = self.items[item_idx.index()].width; if next_next.is_some() { self.items[next_next.index()].prev = item_idx; } // Add next to the free list. self.remove_item(next); next = next_next } if prev.is_some() && !self.items[prev.index()].allocated { // Merge the item into the previous one. self.items[prev.index()].next = next; self.items[prev.index()].width += width; if next.is_some() { self.items[next.index()].prev = prev; } // Add item_idx to the free list. self.remove_item(item_idx); prev = self.items[prev.index()].prev; } if prev.is_none() && next.is_none() { let shelf_idx = shelf; // The shelf is now empty. self.shelves[shelf_idx.index()].is_empty = true; // Only attempt to merge shelves on the same column. let x = self.shelves[shelf_idx.index()].x; let next_shelf = self.shelves[shelf_idx.index()].next; if next_shelf.is_some() && self.shelves[next_shelf.index()].is_empty && self.shelves[next_shelf.index()].x == x { // Merge the next shelf into this one. let next_next = self.shelves[next_shelf.index()].next; let next_height = self.shelves[next_shelf.index()].height; self.shelves[shelf_idx.index()].next = next_next; self.shelves[shelf_idx.index()].height += next_height; if next_next.is_some() { self.shelves[next_next.index()].prev = shelf_idx; } // Add next to the free list. self.remove_shelf(next_shelf); } let prev_shelf = self.shelves[shelf_idx.index()].prev; if prev_shelf.is_some() && self.shelves[prev_shelf.index()].is_empty && self.shelves[prev_shelf.index()].x == x { // Merge the shelf into the previous one. let next_shelf = self.shelves[shelf_idx.index()].next; self.shelves[prev_shelf.index()].next = next_shelf; self.shelves[prev_shelf.index()].height += self.shelves[shelf_idx.index()].height; self.shelves[prev_shelf.index()].next = self.shelves[shelf_idx.index()].next; if next_shelf.is_some() { self.shelves[next_shelf.index()].prev = prev_shelf; } // Add the shelf to the free list. self.remove_shelf(shelf_idx); } } self.check(); } pub fn is_empty(&self) -> bool { let mut shelf_idx = self.first_shelf; while shelf_idx.is_some() { let shelf = &self.shelves[shelf_idx.index()]; if !shelf.is_empty { return false; } shelf_idx = shelf.next; } true } /// Amount of occupied space in the atlas. pub fn allocated_space(&self) -> i32 { self.allocated_space } /// How much space is available for future allocations. pub fn free_space(&self) -> i32 { self.size.area() - self.allocated_space } pub fn iter(&self) -> Iter { Iter { atlas: self, idx: 0, } } fn remove_item(&mut self, idx: ItemIndex) { self.items[idx.index()].next = self.free_items; self.free_items = idx; } fn remove_shelf(&mut self, idx: ShelfIndex) { // Remove the shelf's item. self.remove_item(self.shelves[idx.index()].first_item); self.shelves[idx.index()].next = self.free_shelves; self.free_shelves = idx; } fn add_item(&mut self, mut item: Item) -> ItemIndex { if self.free_items.is_some() { let idx = self.free_items; item.generation = self.items[idx.index()].generation.wrapping_add(1); self.free_items = self.items[idx.index()].next; self.items[idx.index()] = item; return idx; } let idx = ItemIndex(self.items.len() as u16); self.items.push(item); idx } fn add_shelf(&mut self, shelf: Shelf) -> ShelfIndex { if self.free_shelves.is_some() { let idx = self.free_shelves; self.free_shelves = self.shelves[idx.index()].next; self.shelves[idx.index()] = shelf; return idx; } let idx = ShelfIndex(self.shelves.len() as u16); self.shelves.push(shelf); idx } #[cfg(not(feature = "checks"))] fn check(&self) {} #[cfg(feature = "checks")] fn check(&self) { let mut prev_empty = false; let mut accum_h = 0; let mut shelf_idx = self.first_shelf; let mut shelf_x = 0; while shelf_idx.is_some() { let shelf = &self.shelves[shelf_idx.index()]; let new_column = shelf_x != shelf.x; if new_column { assert_eq!(accum_h as i32, self.size.height); accum_h = 0; } shelf_x = shelf.x; accum_h += shelf.height; if prev_empty && !new_column { assert!(!shelf.is_empty); } if shelf.is_empty { assert!(!self.items[shelf.first_item.index()].allocated); assert!(self.items[shelf.first_item.index()].next.is_none()); } prev_empty = shelf.is_empty; let mut accum_w = 0; let mut prev_allocated = true; let mut item_idx = shelf.first_item; let mut prev_item_idx = ItemIndex::NONE; while item_idx.is_some() { let item = &self.items[item_idx.index()]; accum_w += item.width; assert_eq!(item.prev, prev_item_idx); if !prev_allocated { assert!(item.allocated, "item {:?} should be allocated", item_idx.0); } prev_allocated = item.allocated; prev_item_idx = item_idx; item_idx = item.next; } assert_eq!(accum_w, self.shelf_width); shelf_idx = shelf.next; } } /// Turn a valid AllocId into an index that can be used as a key for external storage. /// /// The allocator internally stores all items in a single vector. In addition allocations /// stay at the same index in the vector until they are deallocated. As a result the index /// of an item can be used as a key for external storage using vectors. Note that: /// - The provided ID must correspond to an item that is currently allocated in the atlas. /// - After an item is deallocated, its index may be reused by a future allocation, so /// the returned index should only be considered valid during the lifetime of the its /// associated item. /// - indices are expected to be "reasonable" with respect to the number of allocated items, /// in other words it is never larger than the maximum number of allocated items in the /// atlas (making it a good fit for indexing within a sparsely populated vector). pub fn get_index(&self, id: AllocId) -> u32 { let index = id.index(); debug_assert_eq!(self.items[index as usize].generation, id.generation()); index as u32 } /// Returns the allocation info associated to the allocation ID. /// /// The id must correspond to an existing allocation in the atlas. pub fn get(&self, id: AllocId) -> Rectangle { let index = id.index()as usize; let item = &self.items[index]; assert!(item.allocated); assert_eq!(item.generation, id.generation(), "Invalid AllocId"); let shelf = &self.shelves[item.shelf.index()]; let mut rectangle = Rectangle { min: point2( item.x as i32, shelf.y as i32, ), max: point2( (item.x + item.width) as i32, (shelf.y + shelf.height) as i32, ), }; if self.flip_xy { std::mem::swap(&mut rectangle.min.x, &mut rectangle.min.y); std::mem::swap(&mut rectangle.max.x, &mut rectangle.max.y); } rectangle } /// Dump a visual representation of the atlas in SVG format. pub fn dump_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { use svg_fmt::*; writeln!( output, "{}", BeginSvg { w: self.size.width as f32, h: self.size.height as f32 } )?; self.dump_into_svg(None, output)?; writeln!(output, "{}", EndSvg) } /// Dump a visual representation of the atlas in SVG, omitting the beginning and end of the /// SVG document, so that it can be included in a larger document. /// /// If a rectangle is provided, translate and scale the output to fit it. pub fn dump_into_svg(&self, rect: Option<&Rectangle>, output: &mut dyn std::io::Write) -> std::io::Result<()> { use svg_fmt::*; let (sx, sy, tx, ty) = if let Some(rect) = rect { ( rect.size().width as f32 / self.size.width as f32, rect.size().height as f32 / self.size.height as f32, rect.min.x as f32, rect.min.y as f32, ) } else { (1.0, 1.0, 0.0, 0.0) }; writeln!( output, r#" {}"#, rectangle(tx, ty, self.size.width as f32 * sx, self.size.height as f32 * sy) .fill(rgb(40, 40, 40)) .stroke(Stroke::Color(black(), 1.0)) )?; let mut shelf_idx = self.first_shelf; while shelf_idx.is_some() { let shelf = &self.shelves[shelf_idx.index()]; let y = shelf.y as f32 * sy; let h = shelf.height as f32 * sy; let mut item_idx = shelf.first_item; while item_idx.is_some() { let item = &self.items[item_idx.index()]; let x = item.x as f32 * sx; let w = item.width as f32 * sx; let color = if item.allocated { rgb(70, 70, 180) } else { rgb(50, 50, 50) }; let (x, y) = if self.flip_xy { (y, x) } else { (x, y) }; let (w, h) = if self.flip_xy { (h, w) } else { (w, h) }; writeln!( output, r#" {}"#, rectangle(x + tx, y + ty, w, h).fill(color).stroke(Stroke::Color(black(), 1.0)) )?; item_idx = item.next; } shelf_idx = shelf.next; } Ok(()) } } fn adjust_size(alignment: i32, size: &mut i32) { let rem = *size % alignment; if rem > 0 { *size += alignment - rem; } } fn convert_coordinates(flip_xy: bool, x: i32, y: i32) -> (i32, i32) { if flip_xy { (y, x) } else { (x, y) } } fn shelf_height(size: i32, atlas_height: i32) -> i32 { let alignment = match size { 0 ..= 31 => 8, 32 ..= 127 => 16, 128 ..= 511 => 32, _ => 64, }; let mut adjusted_size = size; let rem = size % alignment; if rem > 0 { adjusted_size = size + alignment - rem; if adjusted_size > atlas_height { adjusted_size = size; } } adjusted_size } /// Iterator over the allocations of an atlas. pub struct Iter<'l> { atlas: &'l AtlasAllocator, idx: usize, } impl<'l> Iterator for Iter<'l> { type Item = Allocation; fn next(&mut self) -> Option { if self.idx >= self.atlas.items.len() { return None; } while !self.atlas.items[self.idx].allocated { self.idx += 1; if self.idx >= self.atlas.items.len() { return None; } } let item = &self.atlas.items[self.idx]; let shelf = &self.atlas.shelves[item.shelf.index()]; let mut alloc = Allocation { rectangle: Rectangle { min: point2( item.x as i32, shelf.y as i32, ), max: point2( (item.x + item.width) as i32, (shelf.y + shelf.height) as i32, ), }, id: AllocId::new(self.idx as u16, item.generation), }; if self.atlas.flip_xy { std::mem::swap(&mut alloc.rectangle.min.x, &mut alloc.rectangle.min.y); std::mem::swap(&mut alloc.rectangle.max.x, &mut alloc.rectangle.max.y); } self.idx += 1; Some(alloc) } } impl<'l> std::iter::IntoIterator for &'l AtlasAllocator { type Item = Allocation; type IntoIter = Iter<'l>; fn into_iter(self) -> Iter<'l> { self.iter() } } #[test] fn test_simple() { let mut atlas = AtlasAllocator::with_options( size2(2048, 2048), &AllocatorOptions { alignment: size2(4, 8), vertical_shelves: false, num_columns: 2, }, ); assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); let a1 = atlas.allocate(size2(20, 30)).unwrap(); let a2 = atlas.allocate(size2(30, 40)).unwrap(); let a3 = atlas.allocate(size2(20, 30)).unwrap(); assert!(a1.id != a2.id); assert!(a1.id != a3.id); assert!(!atlas.is_empty()); //atlas.dump_svg(&mut std::fs::File::create("tmp.svg").expect("!!")).unwrap(); atlas.deallocate(a1.id); atlas.deallocate(a2.id); atlas.deallocate(a3.id); assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); } #[test] fn test_options() { let alignment = size2(8, 16); let mut atlas = AtlasAllocator::with_options( size2(2000, 1000), &AllocatorOptions { alignment, vertical_shelves: true, num_columns: 1, }, ); assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); let a1 = atlas.allocate(size2(20, 30)).unwrap(); let a2 = atlas.allocate(size2(30, 40)).unwrap(); let a3 = atlas.allocate(size2(20, 30)).unwrap(); assert!(a1.id != a2.id); assert!(a1.id != a3.id); assert!(!atlas.is_empty()); for id in &atlas { assert!(id == a1 || id == a2 || id == a3); } assert_eq!(a1.rectangle.min.x % alignment.width, 0); assert_eq!(a1.rectangle.min.y % alignment.height, 0); assert_eq!(a2.rectangle.min.x % alignment.width, 0); assert_eq!(a2.rectangle.min.y % alignment.height, 0); assert_eq!(a3.rectangle.min.x % alignment.width, 0); assert_eq!(a3.rectangle.min.y % alignment.height, 0); assert!(a1.rectangle.size().width >= 20); assert!(a1.rectangle.size().height >= 30); assert!(a2.rectangle.size().width >= 30); assert!(a2.rectangle.size().height >= 40); assert!(a3.rectangle.size().width >= 20); assert!(a3.rectangle.size().height >= 30); //atlas.dump_svg(&mut std::fs::File::create("tmp.svg").expect("!!")).unwrap(); atlas.deallocate(a1.id); atlas.deallocate(a2.id); atlas.deallocate(a3.id); assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); } #[test] fn vertical() { let mut atlas = AtlasAllocator::with_options(size2(128, 256), &AllocatorOptions { num_columns: 2, vertical_shelves: true, ..DEFAULT_OPTIONS }); assert_eq!(atlas.size(), size2(128, 256)); let a = atlas.allocate(size2(32, 16)).unwrap(); let b = atlas.allocate(size2(16, 32)).unwrap(); assert!(a.rectangle.size().width >= 32); assert!(a.rectangle.size().height >= 16); assert!(b.rectangle.size().width >= 16); assert!(b.rectangle.size().height >= 32); let c = atlas.allocate(size2(128, 128)).unwrap(); for _ in &atlas {} atlas.deallocate(a.id); atlas.deallocate(b.id); atlas.deallocate(c.id); for _ in &atlas {} assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); } #[test] fn clear() { let mut atlas = AtlasAllocator::new(size2(2048, 2048)); // Run a workload a few hundred times to make sure clearing properly resets everything. for _ in 0..500 { atlas.clear(); assert_eq!(atlas.allocated_space(), 0); atlas.allocate(size2(8, 2)).unwrap(); atlas.allocate(size2(2, 8)).unwrap(); atlas.allocate(size2(16, 512)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(2, 8)).unwrap(); atlas.allocate(size2(2, 8)).unwrap(); atlas.allocate(size2(8, 2)).unwrap(); atlas.allocate(size2(2, 8)).unwrap(); atlas.allocate(size2(8, 2)).unwrap(); atlas.allocate(size2(8, 8)).unwrap(); atlas.allocate(size2(8, 8)).unwrap(); atlas.allocate(size2(8, 8)).unwrap(); atlas.allocate(size2(8, 8)).unwrap(); atlas.allocate(size2(82, 80)).unwrap(); atlas.allocate(size2(56, 56)).unwrap(); atlas.allocate(size2(64, 66)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(40, 40)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(155, 52)).unwrap(); atlas.allocate(size2(256, 52)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(24, 24)).unwrap(); atlas.allocate(size2(64, 64)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(84, 84)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(8, 2)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(34, 34)).unwrap(); atlas.allocate(size2(192, 192)).unwrap(); atlas.allocate(size2(192, 192)).unwrap(); atlas.allocate(size2(52, 52)).unwrap(); atlas.allocate(size2(144, 144)).unwrap(); atlas.allocate(size2(192, 192)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(144, 144)).unwrap(); atlas.allocate(size2(24, 24)).unwrap(); atlas.allocate(size2(192, 192)).unwrap(); atlas.allocate(size2(192, 192)).unwrap(); atlas.allocate(size2(432, 243)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); atlas.allocate(size2(8, 2)).unwrap(); atlas.allocate(size2(2, 8)).unwrap(); atlas.allocate(size2(9, 9)).unwrap(); atlas.allocate(size2(14, 14)).unwrap(); atlas.allocate(size2(14, 14)).unwrap(); atlas.allocate(size2(14, 14)).unwrap(); atlas.allocate(size2(14, 14)).unwrap(); atlas.allocate(size2(8, 8)).unwrap(); atlas.allocate(size2(27, 27)).unwrap(); atlas.allocate(size2(27, 27)).unwrap(); atlas.allocate(size2(27, 27)).unwrap(); atlas.allocate(size2(27, 27)).unwrap(); atlas.allocate(size2(11, 12)).unwrap(); atlas.allocate(size2(29, 28)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); for _ in &atlas {} } } #[test] fn fuzz_01() { let s = 65472; let mut atlas = AtlasAllocator::new(size2(s, 64)); let alloc = atlas.allocate(size2(s, 64)).unwrap(); assert_eq!(alloc.rectangle.size().width, s); assert_eq!(alloc.rectangle.size().height, 64); let mut atlas = AtlasAllocator::new(size2(64, s)); let alloc = atlas.allocate(size2(64, s)).unwrap(); assert_eq!(alloc.rectangle.size().width, 64); assert_eq!(alloc.rectangle.size().height, s); let mut atlas = AtlasAllocator::new(size2(s, 64)); let alloc = atlas.allocate(size2(s - 1, 64)).unwrap(); assert_eq!(alloc.rectangle.size().width, s); assert_eq!(alloc.rectangle.size().height, 64); let mut atlas = AtlasAllocator::new(size2(64, s)); let alloc = atlas.allocate(size2(64, s - 1)).unwrap(); assert_eq!(alloc.rectangle.size().width, 64); assert_eq!(alloc.rectangle.size().height, s); // Because of potential alignment we won't necessarily // succeed at allocation something this big let s = std::u16::MAX as i32; let mut atlas = AtlasAllocator::new(size2(s, 64)); if let Some(alloc) = atlas.allocate(size2(s, 64)) { assert_eq!(alloc.rectangle.size().width, s); assert_eq!(alloc.rectangle.size().height, 64); } let mut atlas = AtlasAllocator::new(size2(64, s)); if let Some(alloc) = atlas.allocate(size2(64, s)) { assert_eq!(alloc.rectangle.size().width, 64); assert_eq!(alloc.rectangle.size().height, s); } } #[test] fn fuzz_02() { let mut atlas = AtlasAllocator::new(size2(1000, 1000)); assert!(atlas.allocate(size2(255, 65599)).is_none()); } #[test] fn fuzz_03() { let mut atlas = AtlasAllocator::new(size2(1000, 1000)); let sizes = &[ size2(999, 128), size2(168492810, 10), size2(45, 96), size2(-16711926, 0), ]; let mut allocations = Vec::new(); let mut allocated_space = 0; for size in sizes { if let Some(alloc) = atlas.allocate(*size) { allocations.push(alloc); allocated_space += alloc.rectangle.area(); assert_eq!(allocated_space, atlas.allocated_space()); } } for alloc in &allocations { atlas.deallocate(alloc.id); allocated_space -= alloc.rectangle.area(); assert_eq!(allocated_space, atlas.allocated_space()); } assert_eq!(atlas.allocated_space(), 0); } #[test] fn fuzz_04() { let mut atlas = AtlasAllocator::new(size2(1000, 1000)); assert!(atlas.allocate(size2(2560, 2147483647)).is_none()); } #[test] fn issue_17_1() { let mut atlas = AtlasAllocator::new(size2(1024, 1024)); let a = atlas.allocate(size2(100, 300)).unwrap(); let b = atlas.allocate(size2(500, 200)).unwrap(); assert_eq!(a.rectangle, atlas.get(a.id)); assert_eq!(b.rectangle, atlas.get(b.id)); atlas.deallocate(a.id); let c = atlas.allocate(size2(300, 200)).unwrap(); assert_eq!(b.rectangle, atlas.get(b.id)); assert_eq!(c.rectangle, atlas.get(c.id)); atlas.deallocate(c.id); atlas.deallocate(b.id); } #[test] fn issue_17_2() { let mut atlas = AtlasAllocator::new(size2(1000, 1000)); assert!(atlas.allocate(size2(100, 1001)).is_none()); assert!(atlas.allocate(size2(1001, 1000)).is_none()); let a = atlas.allocate(size2(1000, 1000)).unwrap(); assert_eq!(a.rectangle, atlas.get(a.id)); atlas.deallocate(a.id); }