use anyhow::{Context, Result};
use image::GenericImageView;
use super::ansi_parse::{Cell, CellColor, CellGrid, TerminalEmulator};
use super::RenderOptions;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum VAlign {
#[default]
Top,
Center,
Bottom,
}
pub fn render_text_splash(
text: &str,
cols: usize,
rows: usize,
center: bool,
v_align: VAlign,
opts: &RenderOptions,
) -> Result<Vec<u8>> {
let grid = text_to_grid(text, cols, rows, center, v_align);
super::render_grid_to_png(&grid, opts)
}
pub fn render_ansi_splash(
content: &[u8],
cols: usize,
rows: usize,
_center: bool,
v_align: VAlign,
opts: &RenderOptions,
) -> Result<Vec<u8>> {
let mut emu = TerminalEmulator::new(cols, rows);
emu.feed(content);
let grid = emu.snapshot();
if v_align == VAlign::Top {
return super::render_grid_to_png(&grid, opts);
}
let used_rows = grid
.rows
.iter()
.rposition(|row| row.iter().any(|c| c.ch != ' '))
.map_or(0, |r| r + 1);
let v_offset = if used_rows < rows {
match v_align {
VAlign::Center => (rows - used_rows) / 2,
VAlign::Bottom => rows - used_rows,
VAlign::Top => unreachable!(),
}
} else {
0
};
if v_offset == 0 {
return super::render_grid_to_png(&grid, opts);
}
let mut padded = CellGrid {
cols,
rows: Vec::with_capacity(rows),
};
let empty_row = vec![Cell::default(); cols];
for _ in 0..v_offset {
padded.rows.push(empty_row.clone());
}
for row in grid.rows.into_iter().take(rows - v_offset) {
padded.rows.push(row);
}
while padded.rows.len() < rows {
padded.rows.push(empty_row.clone());
}
super::render_grid_to_png(&padded, opts)
}
pub fn render_image_splash(
image_data: &[u8],
cols: usize,
rows: usize,
center: bool,
v_align: VAlign,
opts: &RenderOptions,
) -> Result<Vec<u8>> {
let empty_grid = CellGrid {
cols,
rows: vec![vec![Cell::default(); cols]; rows],
};
let bg_png = super::render_grid_to_png(&empty_grid, opts)?;
let bg_img = image::load_from_memory(&bg_png).context("failed to decode background PNG")?;
let overlay_img =
image::load_from_memory(image_data).context("failed to decode overlay image")?;
let mut canvas = bg_img.to_rgba8();
let (canvas_w, canvas_h) = canvas.dimensions();
let padding = 32u32; let chrome_height = 40u32;
let content_w = canvas_w.saturating_sub(padding);
let content_h = canvas_h.saturating_sub(chrome_height + padding);
let (ow, oh) = overlay_img.dimensions();
let scale = f64::min(content_w as f64 / ow as f64, content_h as f64 / oh as f64).min(1.0);
let scaled_w = (ow as f64 * scale) as u32;
let scaled_h = (oh as f64 * scale) as u32;
let resized = image::imageops::resize(
&overlay_img.to_rgba8(),
scaled_w,
scaled_h,
image::imageops::FilterType::Lanczos3,
);
let x = if center {
((canvas_w - scaled_w) / 2) as i64
} else {
padding as i64 / 2
};
let y = match v_align {
VAlign::Top => (chrome_height + padding / 2) as i64,
VAlign::Center => chrome_height as i64 + (content_h as i64 - scaled_h as i64) / 2,
VAlign::Bottom => chrome_height as i64 + content_h as i64 - scaled_h as i64,
};
image::imageops::overlay(&mut canvas, &resized, x, y);
let mut buf = std::io::Cursor::new(Vec::new());
canvas
.write_to(&mut buf, image::ImageFormat::Png)
.context("failed to encode splash PNG")?;
Ok(buf.into_inner())
}
fn text_to_grid(text: &str, cols: usize, rows: usize, center: bool, v_align: VAlign) -> CellGrid {
let lines: Vec<&str> = text.lines().collect();
let num_lines = lines.len();
let v_offset = if num_lines < rows {
match v_align {
VAlign::Top => 0,
VAlign::Center => (rows - num_lines) / 2,
VAlign::Bottom => rows - num_lines,
}
} else {
0
};
let mut grid_rows = Vec::with_capacity(rows);
for r in 0..rows {
let line_idx = r.checked_sub(v_offset);
let line = line_idx.and_then(|i| lines.get(i)).copied().unwrap_or("");
let chars: Vec<char> = line.chars().collect();
let h_offset = if center && chars.len() < cols {
(cols - chars.len()) / 2
} else {
0
};
let mut row = vec![Cell::default(); cols];
for (i, ch) in chars.iter().enumerate() {
let col = h_offset + i;
if col < cols {
row[col] = Cell {
ch: *ch,
fg: CellColor::Default,
bg: CellColor::Default,
bold: false,
italic: false,
underline: false,
inverse: false,
};
}
}
grid_rows.push(row);
}
CellGrid {
cols,
rows: grid_rows,
}
}