cadmus_core/framebuffer/
mod.rs

1mod image;
2mod ion_sys;
3mod kobo1;
4mod kobo2;
5mod linuxfb_sys;
6mod mxcfb_sys;
7mod sunxi_sys;
8mod transform;
9
10use crate::color::{Color, BLACK, WHITE};
11use crate::geom::{lerp, nearest_segment_point, surface_area, Point, Rectangle};
12use crate::geom::{BorderSpec, ColorSource, CornerSpec, Vec2};
13use anyhow::Error;
14
15pub use self::image::Pixmap;
16pub use self::kobo1::KoboFramebuffer1;
17pub use self::kobo2::KoboFramebuffer2;
18
19#[derive(Debug, Copy, Clone)]
20pub struct Display {
21    pub dims: (u32, u32),
22    pub rotation: i8,
23}
24
25#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
26pub enum UpdateMode {
27    Gui,
28    Partial,
29    Full,
30    Fast,
31    FastMono,
32}
33
34pub trait Framebuffer {
35    fn set_pixel(&mut self, x: u32, y: u32, color: Color);
36    fn set_blended_pixel(&mut self, x: u32, y: u32, color: Color, alpha: f32);
37    fn invert_region(&mut self, rect: &Rectangle);
38    fn shift_region(&mut self, rect: &Rectangle, drift: u8);
39    fn update(&mut self, rect: &Rectangle, mode: UpdateMode) -> Result<u32, Error>;
40    fn wait(&self, token: u32) -> Result<i32, Error>;
41    fn save(&self, path: &str) -> Result<(), Error>;
42    fn set_rotation(&mut self, n: i8) -> Result<(u32, u32), Error>;
43    fn set_monochrome(&mut self, enable: bool);
44    fn set_dithered(&mut self, enable: bool);
45    fn set_inverted(&mut self, enable: bool);
46    fn monochrome(&self) -> bool;
47    fn dithered(&self) -> bool;
48    fn inverted(&self) -> bool;
49    fn width(&self) -> u32;
50    fn height(&self) -> u32;
51
52    fn toggle_inverted(&mut self) {
53        self.set_inverted(!self.inverted());
54    }
55
56    fn toggle_monochrome(&mut self) {
57        self.set_monochrome(!self.monochrome());
58    }
59
60    fn toggle_dithered(&mut self) {
61        self.set_dithered(!self.dithered());
62    }
63
64    fn rotation(&self) -> i8 {
65        0
66    }
67
68    fn dims(&self) -> (u32, u32) {
69        (self.width(), self.height())
70    }
71
72    fn rect(&self) -> Rectangle {
73        let (width, height) = self.dims();
74        rect![0, 0, width as i32, height as i32]
75    }
76
77    fn clear(&mut self, color: Color) {
78        let rect = self.rect();
79        self.draw_rectangle(&rect, color);
80    }
81
82    fn draw_rectangle(&mut self, rect: &Rectangle, color: Color) {
83        for y in rect.min.y..rect.max.y {
84            for x in rect.min.x..rect.max.x {
85                self.set_pixel(x as u32, y as u32, color);
86            }
87        }
88    }
89
90    fn draw_blended_rectangle(&mut self, rect: &Rectangle, color: Color, alpha: f32) {
91        for y in rect.min.y..rect.max.y {
92            for x in rect.min.x..rect.max.x {
93                self.set_blended_pixel(x as u32, y as u32, color, alpha);
94            }
95        }
96    }
97
98    fn draw_rectangle_outline(&mut self, rect: &Rectangle, border: &BorderSpec) {
99        let BorderSpec {
100            thickness: border_thickness,
101            color: border_color,
102        } = *border;
103        self.draw_rectangle(
104            &rect![
105                rect.min.x,
106                rect.min.y,
107                rect.max.x - border_thickness as i32,
108                rect.min.y + border_thickness as i32
109            ],
110            border_color,
111        );
112        self.draw_rectangle(
113            &rect![
114                rect.max.x - border_thickness as i32,
115                rect.min.y,
116                rect.max.x,
117                rect.max.y - border_thickness as i32
118            ],
119            border_color,
120        );
121        self.draw_rectangle(
122            &rect![
123                rect.min.x + border_thickness as i32,
124                rect.max.y - border_thickness as i32,
125                rect.max.x,
126                rect.max.y
127            ],
128            border_color,
129        );
130        self.draw_rectangle(
131            &rect![
132                rect.min.x,
133                rect.min.y + border_thickness as i32,
134                rect.min.x + border_thickness as i32,
135                rect.max.y
136            ],
137            border_color,
138        );
139    }
140
141    fn draw_pixmap(&mut self, pixmap: &Pixmap, pt: Point) {
142        for y in 0..pixmap.height {
143            for x in 0..pixmap.width {
144                let px = x + pt.x as u32;
145                let py = y + pt.y as u32;
146                let color = pixmap.get_pixel(x, y);
147                self.set_pixel(px, py, color);
148            }
149        }
150    }
151
152    fn draw_framed_pixmap(&mut self, pixmap: &Pixmap, rect: &Rectangle, pt: Point) {
153        for y in rect.min.y..rect.max.y {
154            for x in rect.min.x..rect.max.x {
155                let px = x - rect.min.x + pt.x;
156                let py = y - rect.min.y + pt.y;
157                let color = pixmap.get_pixel(x as u32, y as u32);
158                self.set_pixel(px as u32, py as u32, color);
159            }
160        }
161    }
162
163    fn draw_framed_pixmap_contrast(
164        &mut self,
165        pixmap: &Pixmap,
166        rect: &Rectangle,
167        pt: Point,
168        exponent: f32,
169        gray: f32,
170    ) {
171        if (exponent - 1.0).abs() < f32::EPSILON {
172            self.draw_framed_pixmap(pixmap, rect, pt);
173            return;
174        }
175        let rem_gray = 255.0 - gray;
176        let inv_exponent = 1.0 / exponent;
177        for y in rect.min.y..rect.max.y {
178            for x in rect.min.x..rect.max.x {
179                let px = x - rect.min.x + pt.x;
180                let py = y - rect.min.y + pt.y;
181                let raw_color = pixmap.get_pixel(x as u32, y as u32);
182                let color = raw_color.apply(|comp| {
183                    let c = comp as f32;
184                    if c < gray {
185                        (gray * (c / gray).powf(exponent)) as u8
186                    } else if c > gray {
187                        (gray + rem_gray * ((c - gray) / rem_gray).powf(inv_exponent)) as u8
188                    } else {
189                        gray as u8
190                    }
191                });
192                self.set_pixel(px as u32, py as u32, color);
193            }
194        }
195    }
196
197    fn draw_framed_pixmap_halftone(&mut self, pixmap: &Pixmap, rect: &Rectangle, pt: Point) {
198        for y in rect.min.y..rect.max.y {
199            for x in rect.min.x..rect.max.x {
200                let px = x - rect.min.x + pt.x;
201                let py = y - rect.min.y + pt.y;
202                let source_color = pixmap.get_pixel(x as u32, y as u32);
203                let color = if source_color == BLACK {
204                    BLACK
205                } else if source_color == WHITE {
206                    WHITE
207                } else {
208                    transform::transform_dither_g2(x as u32, y as u32, source_color)
209                };
210                self.set_pixel(px as u32, py as u32, color);
211            }
212        }
213    }
214
215    fn draw_blended_pixmap(&mut self, pixmap: &Pixmap, pt: Point, color: Color) {
216        for y in 0..pixmap.height {
217            for x in 0..pixmap.width {
218                let px = x + pt.x as u32;
219                let py = y + pt.y as u32;
220                let alpha = (255.0 - pixmap.get_pixel(x, y).gray() as f32) / 255.0;
221                self.set_blended_pixel(px as u32, py as u32, color, alpha);
222            }
223        }
224    }
225
226    fn draw_rounded_rectangle(&mut self, rect: &Rectangle, corners: &CornerSpec, color: Color) {
227        let (nw, ne, se, sw) = match *corners {
228            CornerSpec::Uniform(v) => (v, v, v, v),
229            CornerSpec::North(v) => (v, v, 0, 0),
230            CornerSpec::East(v) => (0, v, v, 0),
231            CornerSpec::South(v) => (0, 0, v, v),
232            CornerSpec::West(v) => (v, 0, 0, v),
233            CornerSpec::Detailed {
234                north_west,
235                north_east,
236                south_east,
237                south_west,
238            } => (north_west, north_east, south_east, south_west),
239        };
240        let nw_c = rect.min + nw;
241        let ne_c = pt!(rect.max.x - ne, rect.min.y + ne);
242        let se_c = rect.max - se;
243        let sw_c = pt!(rect.min.x + sw, rect.max.y - sw);
244        for y in rect.min.y..rect.max.y {
245            for x in rect.min.x..rect.max.x {
246                let mut alpha = 1.0;
247                let mut pole = None;
248                if x < nw_c.x && y < nw_c.y {
249                    pole = Some((nw_c, nw));
250                } else if x >= ne_c.x && y < ne_c.y {
251                    pole = Some((ne_c, ne));
252                } else if x >= se_c.x && y >= se_c.y {
253                    pole = Some((se_c, se));
254                } else if x < sw_c.x && y >= sw_c.y {
255                    pole = Some((sw_c, sw));
256                }
257                if let Some((center, radius)) = pole {
258                    let v = vec2!((x - center.x) as f32, (y - center.y) as f32) + 0.5;
259                    let angle = v.angle();
260                    let dist = v.length() - radius as f32;
261                    alpha = surface_area(dist, angle);
262                }
263                self.set_blended_pixel(x as u32, y as u32, color, alpha);
264            }
265        }
266    }
267
268    fn draw_rounded_rectangle_with_border(
269        &mut self,
270        rect: &Rectangle,
271        corners: &CornerSpec,
272        border: &BorderSpec,
273        color: &dyn ColorSource,
274    ) {
275        let (nw, ne, se, sw) = match *corners {
276            CornerSpec::Uniform(v) => (v, v, v, v),
277            CornerSpec::North(v) => (v, v, 0, 0),
278            CornerSpec::East(v) => (0, v, v, 0),
279            CornerSpec::South(v) => (0, 0, v, v),
280            CornerSpec::West(v) => (v, 0, 0, v),
281            CornerSpec::Detailed {
282                north_west,
283                north_east,
284                south_east,
285                south_west,
286            } => (north_west, north_east, south_east, south_west),
287        };
288
289        let BorderSpec {
290            thickness: border_thickness,
291            color: border_color,
292        } = *border;
293        let nw_c = rect.min + nw;
294        let ne_c = pt!(rect.max.x - ne, rect.min.y + ne);
295        let se_c = rect.max - se;
296        let sw_c = pt!(rect.min.x + sw, rect.max.y - sw);
297
298        for y in rect.min.y..rect.max.y {
299            for x in rect.min.x..rect.max.x {
300                let mut alpha = 1.0;
301                let mut pole = None;
302                let mut color = color.color(x, y);
303                if x < nw_c.x && y < nw_c.y {
304                    pole = Some((nw_c, nw));
305                } else if x >= ne_c.x && y < ne_c.y {
306                    pole = Some((ne_c, ne));
307                } else if x >= se_c.x && y >= se_c.y {
308                    pole = Some((se_c, se));
309                } else if x < sw_c.x && y >= sw_c.y {
310                    pole = Some((sw_c, sw));
311                }
312                if let Some((center, radius)) = pole {
313                    let small_radius = radius - border_thickness as i32;
314                    let mid_radius = 0.5 * (radius as f32 + small_radius as f32);
315                    let v = vec2!((x - center.x) as f32, (y - center.y) as f32) + 0.5;
316                    let angle = v.angle();
317                    let dist = v.length();
318                    if dist < mid_radius {
319                        let delta_dist = small_radius as f32 - dist;
320                        alpha = surface_area(delta_dist, angle);
321                        color = color.lerp(border_color, alpha);
322                        alpha = 1.0;
323                    } else {
324                        let delta_dist = dist - radius as f32;
325                        color = border_color;
326                        alpha = surface_area(delta_dist, angle);
327                    }
328                } else if x < rect.min.x + border_thickness as i32
329                    || x >= rect.max.x - border_thickness as i32
330                    || y < rect.min.y + border_thickness as i32
331                    || y >= rect.max.y - border_thickness as i32
332                {
333                    color = border_color;
334                }
335                self.set_blended_pixel(x as u32, y as u32, color, alpha);
336            }
337        }
338    }
339
340    fn draw_triangle(&mut self, triangle: &[Point], color: Color) {
341        let mut x_min = ::std::i32::MAX;
342        let mut x_max = ::std::i32::MIN;
343        let mut y_min = ::std::i32::MAX;
344        let mut y_max = ::std::i32::MIN;
345
346        for p in triangle.iter() {
347            if p.x < x_min {
348                x_min = p.x;
349            }
350            if p.x > x_max {
351                x_max = p.x;
352            }
353            if p.y < y_min {
354                y_min = p.y;
355            }
356            if p.y > y_max {
357                y_max = p.y;
358            }
359        }
360
361        x_max += 1;
362        y_max += 1;
363
364        let mut a: Vec2 = triangle[0].into();
365        let mut b: Vec2 = triangle[1].into();
366        let mut c: Vec2 = triangle[2].into();
367
368        a += 0.5;
369        b += 0.5;
370        c += 0.5;
371
372        let ab = b - a;
373        let ac = c - a;
374        let bc = c - b;
375
376        for y in y_min..y_max {
377            for x in x_min..x_max {
378                let p = vec2!(x as f32 + 0.5, y as f32 + 0.5);
379                let ap = p - a;
380                let bp = p - b;
381
382                let s_ab = ab.cross(ap).is_sign_positive();
383                let inside = ac.cross(ap).is_sign_positive() != s_ab
384                    && bc.cross(bp).is_sign_positive() == s_ab;
385
386                let mut dmin = ::std::f32::MAX;
387                let mut nearest = None;
388
389                for &(u, v) in &[(a, b), (b, c), (a, c)] {
390                    let (n, _) = nearest_segment_point(p, u, v);
391                    let d = (n - p).length();
392                    if d < dmin {
393                        dmin = d;
394                        nearest = Some(n);
395                    }
396                }
397
398                if let Some(n) = nearest {
399                    let angle = (n - p).angle();
400                    let delta_dist = if inside { -dmin } else { dmin };
401                    let alpha = surface_area(delta_dist, angle);
402                    self.set_blended_pixel(x as u32, y as u32, color, alpha);
403                }
404            }
405        }
406    }
407
408    fn draw_disk(&mut self, center: Point, radius: i32, color: Color) {
409        let rect = Rectangle::from_disk(center, radius);
410
411        for y in rect.min.y..rect.max.y {
412            for x in rect.min.x..rect.max.x {
413                let v = vec2!((x - center.x) as f32, (y - center.y) as f32) + 0.5;
414                let angle = v.angle();
415                let delta_dist = v.length() - radius as f32;
416                let alpha = surface_area(delta_dist, angle);
417                self.set_blended_pixel(x as u32, y as u32, color, alpha);
418            }
419        }
420    }
421
422    fn draw_segment(
423        &mut self,
424        start: Point,
425        end: Point,
426        start_radius: f32,
427        end_radius: f32,
428        color: Color,
429    ) {
430        let rect = Rectangle::from_segment(
431            start,
432            end,
433            start_radius.ceil() as i32,
434            end_radius.ceil() as i32,
435        );
436        let a = vec2!(start.x as f32, start.y as f32) + 0.5;
437        let b = vec2!(end.x as f32, end.y as f32) + 0.5;
438
439        for y in rect.min.y..rect.max.y {
440            for x in rect.min.x..rect.max.x {
441                let p = vec2!(x as f32, y as f32) + 0.5;
442                let (n, t) = nearest_segment_point(p, a, b);
443                let radius = lerp(start_radius, end_radius, t);
444                if (n - p).length() <= radius {
445                    self.set_pixel(x as u32, y as u32, color);
446                }
447            }
448        }
449    }
450}