egui/epaint/src/texture_atlas.rs

123 lines
3.4 KiB
Rust
Raw Normal View History

2022-01-22 10:23:12 +00:00
use crate::{AlphaImage, ImageDelta};
#[derive(Clone, Copy, Eq, PartialEq)]
struct Rectu {
/// inclusive
min_x: usize,
/// inclusive
min_y: usize,
/// exclusive
max_x: usize,
/// exclusive
max_y: usize,
2019-01-16 23:09:12 +00:00
}
2022-01-22 10:23:12 +00:00
impl Rectu {
const NOTHING: Self = Self {
min_x: usize::MAX,
min_y: usize::MAX,
max_x: 0,
max_y: 0,
};
const EVERYTHING: Self = Self {
min_x: 0,
min_y: 0,
max_x: usize::MAX,
max_y: usize::MAX,
};
2019-01-16 23:09:12 +00:00
}
2020-07-23 12:35:12 +00:00
/// Contains font data in an atlas, where each character occupied a small rectangle.
///
/// More characters can be added, possibly expanding the texture.
2022-01-22 10:23:12 +00:00
#[derive(Clone)]
pub struct TextureAtlas {
2022-01-22 10:23:12 +00:00
image: AlphaImage,
/// What part of the image that is dirty
dirty: Rectu,
/// Used for when allocating new rectangles.
cursor: (usize, usize),
row_height: usize,
}
impl TextureAtlas {
pub fn new(size: [usize; 2]) -> Self {
Self {
2022-01-22 10:23:12 +00:00
image: AlphaImage::new(size),
dirty: Rectu::EVERYTHING,
cursor: (0, 0),
row_height: 0,
}
}
2022-01-22 10:23:12 +00:00
pub fn size(&self) -> [usize; 2] {
self.image.size
}
2022-01-22 10:23:12 +00:00
/// Call to get the change to the image since last call.
pub fn take_delta(&mut self) -> Option<ImageDelta> {
let dirty = std::mem::replace(&mut self.dirty, Rectu::NOTHING);
if dirty == Rectu::NOTHING {
None
} else if dirty == Rectu::EVERYTHING {
Some(ImageDelta::full(self.image.clone()))
} else {
let pos = [dirty.min_x, dirty.min_y];
let size = [dirty.max_x - dirty.min_x, dirty.max_y - dirty.min_y];
let region = self.image.region(pos, size);
Some(ImageDelta::partial(pos, region))
}
}
2022-01-22 10:23:12 +00:00
/// Returns the coordinates of where the rect ended up,
/// and invalidates the region.
pub fn allocate(&mut self, (w, h): (usize, usize)) -> ((usize, usize), &mut AlphaImage) {
/// On some low-precision GPUs (my old iPad) characters get muddled up
/// if we don't add some empty pixels between the characters.
/// On modern high-precision GPUs this is not needed.
const PADDING: usize = 1;
assert!(
w <= self.image.width(),
"Tried to allocate a {} wide glyph in a {} wide texture atlas",
w,
self.image.width()
);
if self.cursor.0 + w > self.image.width() {
// New row:
self.cursor.0 = 0;
self.cursor.1 += self.row_height + PADDING;
self.row_height = 0;
}
self.row_height = self.row_height.max(h);
2022-01-22 10:23:12 +00:00
if resize_to_min_height(&mut self.image, self.cursor.1 + self.row_height) {
self.dirty = Rectu::EVERYTHING;
}
let pos = self.cursor;
self.cursor.0 += w + PADDING;
2022-01-22 10:23:12 +00:00
self.dirty.min_x = self.dirty.min_x.min(pos.0);
self.dirty.min_y = self.dirty.min_y.min(pos.1);
self.dirty.max_x = self.dirty.max_x.max(pos.0 + w);
self.dirty.max_y = self.dirty.max_y.max(pos.1 + h);
(pos, &mut self.image)
}
}
2022-01-22 10:23:12 +00:00
fn resize_to_min_height(image: &mut AlphaImage, min_height: usize) -> bool {
while min_height >= image.height() {
image.size[1] *= 2; // double the height
}
if image.width() * image.height() > image.pixels.len() {
image.pixels.resize(image.width() * image.height(), 0);
2022-01-22 10:23:12 +00:00
true
} else {
false
}
}