refactor(v0.2): вынести headless render в shturman-render (shell использует)
P6.1: общий хелпер render_to_png<C: ComponentHandle>(build, w, h, path) поверх Slint software-renderer (thread_local окно + set_platform once + draw + png). shturman-shell.render_screenshot теперь зовёт его; плумбинг-дубль удалён. png в shell → dev-deps (рендер в render-крейте, тест декодирует). Тесты зелёные. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
@@ -6,12 +6,8 @@
|
||||
mod theme;
|
||||
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use slint::platform::software_renderer::{MinimalSoftwareWindow, RepaintBufferType};
|
||||
use slint::platform::{Platform, PlatformError, WindowAdapter};
|
||||
use slint::ComponentHandle;
|
||||
|
||||
slint::slint! {
|
||||
@@ -167,64 +163,13 @@ pub fn run_interactive(initial: &Initial, hour: u8, clock: &str) -> anyhow::Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- headless software-render (спека §6: основной автотест кадра, композитор не нужен) ---
|
||||
|
||||
thread_local! {
|
||||
static SCREENSHOT_WINDOW: Rc<MinimalSoftwareWindow> =
|
||||
MinimalSoftwareWindow::new(RepaintBufferType::ReusedBuffer);
|
||||
}
|
||||
|
||||
struct ScreenshotPlatform;
|
||||
impl Platform for ScreenshotPlatform {
|
||||
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
|
||||
Ok(SCREENSHOT_WINDOW.with(|w| w.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Установить software-platform один раз на процесс. `--screenshot` зовётся в свежем процессе
|
||||
/// первым делом; в мультитестовом процессе повтор терпим (`Once` + терпимый результат).
|
||||
fn ensure_screenshot_platform() {
|
||||
static ONCE: Once = Once::new();
|
||||
ONCE.call_once(|| {
|
||||
let _ = slint::platform::set_platform(Box::new(ScreenshotPlatform));
|
||||
});
|
||||
}
|
||||
// --- headless software-render первого кадра (спека §6) — через общий хелпер shturman-render ---
|
||||
|
||||
/// Headless software-render первого кадра в PNG (спека §6). Без дисплей-сервера/композитора.
|
||||
/// `hour` задаёт тему для `auto` (тест — детерминированно); `clock` берётся из текущего времени.
|
||||
pub fn render_screenshot(initial: &Initial, hour: u8, path: &Path) -> anyhow::Result<()> {
|
||||
ensure_screenshot_platform();
|
||||
let (_, clock) = utc_hh_mm();
|
||||
let ui = build_ui(initial, hour, &clock)?;
|
||||
|
||||
let window = SCREENSHOT_WINDOW.with(|w| w.clone());
|
||||
window.set_size(slint::PhysicalSize::new(FRAME_W, FRAME_H));
|
||||
ui.show()?;
|
||||
ui.window().request_redraw(); // форсим перерисовку (повторный рендер в том же потоке)
|
||||
|
||||
let mut buf = vec![slint::Rgb8Pixel { r: 0, g: 0, b: 0 }; (FRAME_W * FRAME_H) as usize];
|
||||
let drawn = window.draw_if_needed(|renderer| {
|
||||
renderer.render(buf.as_mut_slice(), FRAME_W as usize);
|
||||
});
|
||||
ui.hide()?; // освободить окно для следующего рендера в том же потоке
|
||||
if !drawn {
|
||||
anyhow::bail!("software-renderer не отрисовал кадр");
|
||||
}
|
||||
write_png(path, FRAME_W, FRAME_H, &buf)?;
|
||||
shturman_render::render_to_png(|| build_ui(initial, hour, &clock), FRAME_W, FRAME_H, path)?;
|
||||
tracing::info!(path = %path.display(), "кадр записан (software-render)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_png(path: &Path, w: u32, h: u32, buf: &[slint::Rgb8Pixel]) -> anyhow::Result<()> {
|
||||
let file = std::fs::File::create(path)?;
|
||||
let mut enc = png::Encoder::new(std::io::BufWriter::new(file), w, h);
|
||||
enc.set_color(png::ColorType::Rgb);
|
||||
enc.set_depth(png::BitDepth::Eight);
|
||||
let mut writer = enc.write_header()?;
|
||||
let mut data = Vec::with_capacity((w * h * 3) as usize);
|
||||
for px in buf {
|
||||
data.extend_from_slice(&[px.r, px.g, px.b]);
|
||||
}
|
||||
writer.write_image_data(&data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user