1use std::fmt::Display;
9
10#[derive(Debug, PartialEq)]
11pub struct RectF {
12 pub left: f32,
13 pub top: f32,
14 pub right: f32,
15 pub bottom: f32,
16}
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19pub enum PatchKind {
20 Unknown,
21 Fixed,
22 Stretching,
23 Tiling,
24}
25
26#[derive(Debug, PartialEq)]
27pub struct Section {
28 pub start: f32,
29 pub len: f32,
30 pub kind: PatchKind,
31}
32
33#[derive(Debug, PartialEq)]
34pub struct Patch {
35 pub source: RectF,
36 pub target: RectF,
37 pub h_kind: PatchKind,
38 pub v_kind: PatchKind,
39}
40
41#[derive(Debug)]
42pub struct NinePatchDrawable {
43 pub width: usize,
44 pub height: usize,
45 pub h_sections: Vec<Section>,
46 pub v_sections: Vec<Section>,
47 pub margin_left: f32,
48 pub margin_top: f32,
49 pub margin_right: f32,
50 pub margin_bottom: f32,
51}
52
53#[derive(Debug)]
54pub enum NinePatchError {
55 InvalidBitmap,
56 InvalidMargin,
57}
58
59impl Display for NinePatchError {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 NinePatchError::InvalidBitmap => write!(f, "invalid bitmap"),
63 NinePatchError::InvalidMargin => write!(f, "invalid margin"),
64 }
65 }
66}
67
68impl NinePatchDrawable {
69 pub fn new(
72 bitmap: &[u8],
73 stride: usize,
74 width: usize,
75 height: usize,
76 ) -> Result<NinePatchDrawable, NinePatchError> {
77 if bitmap.len() != stride * height || stride < width * 4 || width < 3 || height < 3 {
78 return Err(NinePatchError::InvalidBitmap);
79 }
80
81 let top_sections = h_sections(bitmap, 0, width);
82 let left_sections = v_sections(bitmap, 0, stride, height);
83 let right_sections = dbg!(v_sections(bitmap, (width - 1) * 4, stride, height));
84 let bottom_sections = dbg!(h_sections(bitmap, (height - 1) * stride, width));
85
86 if right_sections.len() != 0 && right_sections.len() != 3 {
87 return Err(NinePatchError::InvalidMargin);
88 }
89 if bottom_sections.len() != 0 && bottom_sections.len() != 3 {
90 return Err(NinePatchError::InvalidMargin);
91 }
92
93 Ok(NinePatchDrawable {
94 width,
95 height,
96 h_sections: top_sections,
97 v_sections: left_sections,
98 margin_left: bottom_sections.first().map_or(0.0, |s| s.len),
99 margin_top: right_sections.first().map_or(0.0, |s| s.len),
100 margin_right: bottom_sections.last().map_or(0.0, |s| s.len),
101 margin_bottom: right_sections.last().map_or(0.0, |s| s.len),
102 })
103 }
104
105 pub fn scale_to(&self, width: usize, height: usize) -> Vec<Patch> {
106 assert!(width >= self.width && height >= self.height);
107
108 let stretching_width: f32 = self
109 .h_sections
110 .iter()
111 .filter(|s| s.kind != PatchKind::Fixed)
112 .map(|s| s.len)
113 .sum();
114 let stretching_height: f32 = self
115 .v_sections
116 .iter()
117 .filter(|s| s.kind != PatchKind::Fixed)
118 .map(|s| s.len)
119 .sum();
120 let fixed_width = self.width as f32 - stretching_width;
121 let fixed_height = self.height as f32 - stretching_height;
122 let mut left = 1.0;
123 let mut top = 1.0;
124 let mut prev_bottom = 1.0;
125 let mut patches = vec![];
126 for v in &self.v_sections {
127 for h in &self.h_sections {
128 let source = RectF {
129 left: h.start + 1.0,
130 top: v.start + 1.0,
131 right: h.start + 1.0 + h.len,
132 bottom: v.start + 1.0 + v.len,
133 };
134 let right = match h.kind {
135 PatchKind::Fixed => left + h.len,
136 _ => left + (h.len / stretching_width) * (width as f32 - fixed_width),
137 };
138 let bottom = match v.kind {
139 PatchKind::Fixed => top + v.len,
140 _ => top + (v.len / stretching_height) * (height as f32 - fixed_height),
141 };
142 patches.push(Patch {
143 source,
144 target: RectF {
145 left,
146 top,
147 right,
148 bottom,
149 },
150 h_kind: h.kind,
151 v_kind: v.kind,
152 });
153 left = right;
154 prev_bottom = bottom;
155 }
156 left = 1.0;
157 top = prev_bottom;
158 }
159 patches
160 }
161}
162
163fn h_sections(bitmap: &[u8], offset: usize, width: usize) -> Vec<Section> {
164 let mut start = 0.0;
165 let mut len = 0.0;
166 let mut kind = PatchKind::Unknown;
167 let mut sections = vec![];
168 for i in 1..width {
169 let o = offset + i * 4;
170 let i_kind = match (bitmap[o], bitmap[o + 1], bitmap[o + 2]) {
171 (0xFF, 0xFF, 0xFF) => PatchKind::Fixed,
172 _ => PatchKind::Stretching,
173 };
174 if i != (width - 1) && (kind == i_kind || kind == PatchKind::Unknown) {
175 len += 1.0;
176 kind = i_kind;
177 } else {
178 sections.push(Section { start, len, kind });
179 (start, len, kind) = (start + len, 1.0, i_kind);
180 }
181 }
182 sections
183}
184
185fn v_sections(bitmap: &[u8], offset: usize, advance: usize, height: usize) -> Vec<Section> {
186 let mut start = 0.0;
187 let mut len = 0.0;
188 let mut kind = PatchKind::Unknown;
189 let mut sections = vec![];
190 for i in 1..height {
191 let o = offset + i * advance;
192 let i_kind = match (bitmap[o], bitmap[o + 1], bitmap[o + 2]) {
193 (0xFF, 0xFF, 0xFF) => PatchKind::Fixed,
194 _ => PatchKind::Stretching,
195 };
196 if i != (height - 1) && (kind == i_kind || kind == PatchKind::Unknown) {
197 len += 1.0;
198 kind = i_kind;
199 } else {
200 sections.push(Section { start, len, kind });
201 (start, len, kind) = (start + len, 1.0, i_kind);
202 }
203 }
204 sections
205}
206
207#[cfg(test)]
208mod tests;