srs2dge_core/packer/texture/
map.rs

1use crate::prelude::{
2    PositionedRect, Rect, Target, TextureAtlas, TextureAtlasBuilder, TextureAtlasFile,
3    TexturePosition,
4};
5use image::RgbaImage;
6use serde::{Deserialize, Serialize};
7use std::{
8    collections::{BinaryHeap, HashMap},
9    hash::Hash,
10    ops::Deref,
11};
12
13//
14
15#[derive(Debug, Clone)]
16struct SortBySize<K> {
17    key: K,
18    image: RgbaImage,
19}
20
21impl<K> SortBySize<K> {
22    fn size(&self) -> u64 {
23        let (w, h) = self.image.dimensions();
24        w as u64 * h as u64
25    }
26}
27
28impl<K> PartialEq for SortBySize<K> {
29    fn eq(&self, other: &Self) -> bool {
30        self.size() == other.size()
31    }
32}
33
34impl<K> Eq for SortBySize<K> {}
35
36impl<K> PartialOrd for SortBySize<K> {
37    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
38        self.size().partial_cmp(&other.size())
39    }
40}
41
42impl<K> Ord for SortBySize<K> {
43    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
44        self.size().cmp(&other.size())
45    }
46}
47
48#[derive(Debug, Clone)]
49pub struct TextureAtlasMapBuilder<K> {
50    // side length limit
51    limit: u16,
52
53    images: BinaryHeap<SortBySize<K>>,
54}
55
56#[derive(Debug)]
57pub struct TextureAtlasMap<K>
58where
59    K: Eq + Hash + Clone,
60{
61    inner: TextureAtlas,
62    map: HashMap<K, TexturePosition>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct TextureAtlasMapFile<K>
67where
68    K: Eq + Hash + Clone,
69{
70    inner: TextureAtlasFile,
71    map: HashMap<K, TexturePosition>,
72}
73
74impl<K> Default for TextureAtlasMapBuilder<K> {
75    fn default() -> Self {
76        Self {
77            images: Default::default(),
78            limit: u16::MAX,
79        }
80    }
81}
82
83impl<K> TextureAtlasMapBuilder<K> {
84    pub fn new() -> Self {
85        Self::default()
86    }
87
88    /// side length limit
89    pub fn with_limit(mut self, limit: u16) -> Self {
90        self.limit = limit;
91        self
92    }
93
94    pub fn with(mut self, key: K, image: RgbaImage) -> Self {
95        self.insert(key, image);
96        self
97    }
98
99    pub fn insert(&mut self, key: K, image: RgbaImage) {
100        self.images.push(SortBySize { key, image });
101    }
102}
103
104impl<K> TextureAtlasMapBuilder<K>
105where
106    K: Eq + Hash + Clone,
107{
108    pub fn build(mut self, target: &Target) -> TextureAtlasMap<K> {
109        let mut builder = TextureAtlasBuilder::new();
110        let mut images = vec![];
111
112        while let Some(SortBySize { key, image }) = self.images.pop() {
113            let (width, height) = image.dimensions();
114
115            let v = builder
116                .push(Rect { width, height })
117                .expect("Texture atlas limit reached");
118            images.push((key, v, image));
119        }
120
121        type Map<K> = Vec<(K, PositionedRect)>;
122        type Iter = Vec<(RgbaImage, PositionedRect)>;
123        let (map, iter): (Map<K>, Iter) = images
124            .into_iter()
125            .map(|(key, pos, img)| ((key, pos), (img, pos)))
126            .unzip();
127
128        let inner = builder.build(target, iter.into_iter());
129        let size = inner.get_dim();
130        let map = map
131            .into_iter()
132            .map(|(key, rect)| (key, TexturePosition::new(size, rect)))
133            .collect();
134
135        TextureAtlasMap { inner, map }
136    }
137}
138
139impl<K> TextureAtlasMap<K>
140where
141    K: Eq + Hash + Clone,
142{
143    pub async fn convert(&self, target: &Target) -> TextureAtlasMapFile<K> {
144        let inner = self.inner.convert(target).await;
145        let map = self.map.clone();
146
147        TextureAtlasMapFile { inner, map }
148    }
149
150    pub fn get(&self, key: &K) -> Option<TexturePosition> {
151        self.map.get(key).copied()
152    }
153}
154
155impl<K> Deref for TextureAtlasMap<K>
156where
157    K: Eq + Hash + Clone,
158{
159    type Target = TextureAtlas;
160
161    fn deref(&self) -> &Self::Target {
162        &self.inner
163    }
164}
165
166impl<K> TextureAtlasMapFile<K>
167where
168    K: Eq + Hash + Clone,
169{
170    pub fn convert(&self, target: &Target) -> TextureAtlasMap<K> {
171        let inner = self.inner.convert(target);
172        let map = self.map.clone();
173
174        TextureAtlasMap { inner, map }
175    }
176}