devpage_design/png/
mod.rs1use std::io::Write;
7
8use crate::display::List;
9use crate::render::Renderer;
10use crate::scene::{Scene, Theme};
11use crate::tiles::TILE;
12
13pub const BAND_BYTES: usize = 16 << 20;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Piece {
23 Tile { col: u32, row: u32 },
24 Strip { col: u32, y: u32, rows: u32 },
25}
26
27pub fn rows_per_band(width: u32) -> u32 {
31 let padded = width.max(1).div_ceil(TILE) as usize * TILE as usize * 4;
32 (BAND_BYTES / padded).clamp(1, TILE as usize) as u32
33}
34
35pub fn fresh(renderer: &mut Renderer, scene: &Scene, theme: Theme) -> Result<Vec<u8>, String> {
42 let drawing = renderer
43 .drawing(scene, theme, None)
44 .map_err(|e| e.message)?;
45 let mut png = Vec::new();
46 write(&drawing.list, &mut png).map_err(|e| format!("encoding the PNG: {e}"))?;
47 Ok(png)
48}
49
50pub fn write(list: &List, out: impl Write) -> Result<(), png::EncodingError> {
52 let (w, h) = (list.area.width as u32, list.area.height as u32);
53 write_bands(w, h, out, |piece| {
54 Ok::<_, std::convert::Infallible>(match piece {
55 Piece::Tile { col, row } => crate::cpu::tile(list, 1.0, col, row),
56 Piece::Strip { col, y, rows } => crate::cpu::strip(list, col, y, rows),
57 })
58 })
59 .map_err(|e| match e {
60 Band::Encoding(e) => e,
61 })
62}
63
64#[derive(Debug)]
67pub enum Band<E> {
68 Encoding(png::EncodingError),
69 Tile(E),
70}
71
72impl<E: std::fmt::Display> std::fmt::Display for Band<E> {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 Band::Encoding(e) => write!(f, "{e}"),
76 Band::Tile(e) => write!(f, "{e}"),
77 }
78 }
79}
80
81pub fn write_bands<P: AsRef<[u8]>, E>(
86 width: u32,
87 height: u32,
88 out: impl Write,
89 mut piece: impl FnMut(Piece) -> Result<P, E>,
90) -> Result<(), Band<E>> {
91 let (w, h) = (width.max(1), height.max(1));
92 let mut encoder = png::Encoder::new(out, w, h);
93 encoder.set_color(png::ColorType::Rgba);
94 encoder.set_depth(png::BitDepth::Eight);
95 let mut header = encoder.write_header().map_err(Band::Encoding)?;
96 let mut writer = header.stream_writer().map_err(Band::Encoding)?;
97 let cols = w.div_ceil(TILE);
98 let per = rows_per_band(w);
99 let mut y = 0;
100 while y < h {
101 let rows = per.min(h - y);
102 let band = (0..cols)
103 .map(|col| {
104 piece(if per == TILE {
105 Piece::Tile { col, row: y / TILE }
106 } else {
107 Piece::Strip { col, y, rows }
108 })
109 })
110 .collect::<Result<Vec<P>, E>>()
111 .map_err(Band::Tile)?;
112 for r in 0..rows {
113 for (col, p) in band.iter().enumerate() {
114 let across = (w - col as u32 * TILE).min(TILE) as usize;
115 let start = r as usize * TILE as usize * 4;
116 writer
117 .write_all(&p.as_ref()[start..start + across * 4])
118 .map_err(|e| Band::Encoding(e.into()))?;
119 }
120 }
121 y += rows;
122 }
123 writer.finish().map_err(Band::Encoding)
124}
125
126#[cfg(test)]
127mod tests;