1#![cfg_attr(not(test), no_std)]
2#![allow(clippy::tabs_in_doc_comments)]
3#![warn(rust_2018_idioms, unreachable_pub)]
4#![forbid(unsafe_code)]
5
6use core::num::NonZeroU8;
48use embedded_graphics::{
49 draw_target::DrawTarget,
50 geometry::{Dimensions, OriginDimensions, Point, Size},
51 image::{ImageDrawable, ImageRaw},
52 mono_font::mapping::GlyphMapping,
53 pixelcolor::BinaryColor,
54 primitives::Rectangle,
55 text::{
56 renderer::{TextMetrics, TextRenderer},
57 Baseline
58 },
59 Pixel
60};
61
62pub mod tamzen;
63
64#[derive(Clone, Copy)]
66pub struct BitmapFont<'a> {
67 bitmap: ImageRaw<'a, BinaryColor>,
69
70 glyph_mapping: &'a dyn GlyphMapping,
72
73 size: Size,
75
76 pixels: NonZeroU8
79}
80
81impl<'a> BitmapFont<'a> {
82 pub const fn width(self) -> u32 {
84 self.size.width * self.pixels.get() as u32
85 }
86
87 pub const fn height(self) -> u32 {
89 self.size.height * self.pixels.get() as u32
90 }
91
92 pub fn draw_glyph<D>(&self, idx: u32, target: &mut D, color: BinaryColor, pos: Point) -> Result<(), D::Error>
94 where
95 D: DrawTarget<Color = BinaryColor>
96 {
97 let char_per_row = self.bitmap.size().width / self.size.width;
98 let row = idx / char_per_row;
99
100 let char_x = (idx - (row * char_per_row)) * self.size.width;
102 let char_y = row * self.size.height;
103 let area = Rectangle::new(Point::new(char_x as _, char_y as _), self.size);
104
105 let mut pixel_doubling_target = PixelDoublingDrawTarget {
107 target,
108 color,
109 offset: pos,
110 pixels: self.pixels
111 };
112 self.bitmap.draw_sub_image(&mut pixel_doubling_target, &area)?;
113
114 Ok(())
115 }
116
117 pub const fn pixel_double(mut self) -> Self {
119 self.pixels = match NonZeroU8::new(self.pixels.get() * 2) {
121 Some(px) => px,
122 None => unreachable!()
123 };
124 self
125 }
126}
127
128#[derive(Clone, Copy)]
130#[non_exhaustive]
131pub struct TextStyle<'a> {
132 pub font: &'a BitmapFont<'a>,
133 pub color: BinaryColor
134}
135
136impl<'a> TextStyle<'a> {
137 pub fn new(font: &'a BitmapFont<'a>, color: BinaryColor) -> Self {
138 Self { font, color }
139 }
140}
141
142impl<'a> TextRenderer for TextStyle<'a> {
143 type Color = BinaryColor;
144
145 fn draw_string<D>(&self, text: &str, mut pos: Point, _: Baseline, target: &mut D) -> Result<Point, D::Error>
146 where
147 D: DrawTarget<Color = Self::Color>
148 {
149 for c in text.chars() {
150 let glyph_idx = self.font.glyph_mapping.index(c) as u32;
151 self.font.draw_glyph(glyph_idx, target, self.color, pos)?;
152 pos += Size::new(self.font.width(), 0);
153 }
154 Ok(pos)
155 }
156
157 fn draw_whitespace<D>(&self, width: u32, pos: Point, _: Baseline, _: &mut D) -> Result<Point, D::Error>
158 where
159 D: DrawTarget<Color = Self::Color>
160 {
161 Ok(pos + Size::new(width * self.font.width(), 0))
163 }
164
165 fn measure_string(&self, text: &str, pos: Point, _: Baseline) -> TextMetrics {
166 let size = Size::new(self.font.width() * text.len() as u32, self.font.height());
167 TextMetrics {
168 bounding_box: Rectangle::new(pos, size),
169 next_position: pos + Size::new(size.width, 0)
170 }
171 }
172
173 fn line_height(&self) -> u32 {
174 self.font.height()
175 }
176}
177
178struct PixelDoublingDrawTarget<'a, D: DrawTarget<Color = BinaryColor>> {
186 target: &'a mut D,
187 color: BinaryColor,
188 offset: Point,
189 pixels: NonZeroU8
190}
191
192impl<'a, D> Dimensions for PixelDoublingDrawTarget<'a, D>
193where
194 D: DrawTarget<Color = BinaryColor>
195{
196 fn bounding_box(&self) -> Rectangle {
197 let mut bb = self.target.bounding_box();
198 bb.top_left -= self.offset;
199 bb.top_left /= self.pixels.get().into();
200 bb.size /= self.pixels.get().into();
201 bb
202 }
203}
204
205impl<'a, D> DrawTarget for PixelDoublingDrawTarget<'a, D>
206where
207 D: DrawTarget<Color = BinaryColor>
208{
209 type Color = BinaryColor;
210 type Error = D::Error;
211
212 fn draw_iter<I>(&mut self, pixel_iter: I) -> Result<(), Self::Error>
213 where
214 I: IntoIterator<Item = Pixel<BinaryColor>>
215 {
216 let color = self.color;
217 let offset = self.offset;
218 let pixels = self.pixels.get();
219 self.target.draw_iter(
220 pixel_iter
221 .into_iter()
222 .filter(|pixel| pixel.1 == BinaryColor::On)
223 .flat_map(|pixel| PixelDoublingIterator::new(pixel, pixels).map(|pixel| Pixel(pixel.0 + offset, color)))
224 )
225 }
226}
227
228struct PixelDoublingIterator {
229 color: BinaryColor,
230 pos: Point,
231 x: u8,
233 remaining_x: u8,
235 remaining_y: u8
237}
238
239impl PixelDoublingIterator {
240 fn new(pixel: Pixel<BinaryColor>, pixels: u8) -> Self {
241 Self {
242 color: pixel.1,
243 pos: pixel.0 * pixels as _,
244 x: 0,
245 remaining_x: pixels - 1,
246 remaining_y: pixels
247 }
248 }
249}
250
251impl Iterator for PixelDoublingIterator {
252 type Item = Pixel<BinaryColor>;
253
254 fn next(&mut self) -> Option<Pixel<BinaryColor>> {
255 if self.remaining_y == 0 {
256 return None;
257 }
258
259 let pixel = Pixel(self.pos, self.color);
260 if self.remaining_x > 0 {
261 self.remaining_x -= 1;
262 self.x += 1;
263 self.pos.x += 1;
264 } else {
265 self.pos.x -= self.x as i32;
266 self.remaining_x = self.x;
267 self.x = 0;
268
269 self.remaining_y -= 1;
270 self.pos.y += 1;
271 }
272 Some(pixel)
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 fn px(x: i32, y: i32) -> Pixel<BinaryColor> {
281 Pixel(Point::new(x, y), BinaryColor::On)
282 }
283
284 #[test]
285 fn pixel_doubling_iterator() {
286 assert_eq!(PixelDoublingIterator::new(px(0, 0), 1).collect::<Vec<_>>(), vec![px(0, 0)]);
287 assert_eq!(PixelDoublingIterator::new(px(1, 2), 1).collect::<Vec<_>>(), vec![px(1, 2)]);
288
289 assert_eq!(PixelDoublingIterator::new(px(0, 0), 2).collect::<Vec<_>>(), vec![
290 px(0, 0),
291 px(1, 0),
292 px(0, 1),
293 px(1, 1)
294 ]);
295 assert_eq!(PixelDoublingIterator::new(px(1, 2), 2).collect::<Vec<_>>(), vec![
296 px(2, 4),
297 px(3, 4),
298 px(2, 5),
299 px(3, 5)
300 ]);
301
302 assert_eq!(PixelDoublingIterator::new(px(0, 0), 3).collect::<Vec<_>>(), vec![
303 px(0, 0),
304 px(1, 0),
305 px(2, 0),
306 px(0, 1),
307 px(1, 1),
308 px(2, 1),
309 px(0, 2),
310 px(1, 2),
311 px(2, 2)
312 ]);
313 assert_eq!(PixelDoublingIterator::new(px(1, 2), 3).collect::<Vec<_>>(), vec![
314 px(3, 6),
315 px(4, 6),
316 px(5, 6),
317 px(3, 7),
318 px(4, 7),
319 px(5, 7),
320 px(3, 8),
321 px(4, 8),
322 px(5, 8)
323 ]);
324 }
325}