use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler;
use winit::dpi::PhysicalSize;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::{Window, WindowAttributes, WindowId};
use layout_cat::Viewport;
use crate::error::Error;
use crate::frame::Frame;
use crate::raster::{PixelBuffer, render_to_pixels_with};
use crate::text::TextRenderer;
pub fn run_window(frame: Frame, viewport: Viewport) -> Result<(), Error> {
let event_loop = EventLoop::new()?;
let mut app = WindowApp::new(frame, viewport);
event_loop.run_app(&mut app)?;
Ok(())
}
struct WindowApp {
frame: Frame,
viewport: Viewport,
text_renderer: TextRenderer,
window: Option<Window>,
pixels: Option<PixelBuffer>,
}
impl WindowApp {
fn new(frame: Frame, viewport: Viewport) -> Self {
Self {
frame,
viewport,
text_renderer: TextRenderer::new(),
window: None,
pixels: None,
}
}
fn present(&mut self) -> Option<()> {
let window = self.window.as_ref()?;
let pixels = self.pixels.as_ref()?;
let size = window.inner_size();
let width = NonZeroU32::new(size.width)?;
let height = NonZeroU32::new(size.height)?;
let context = Context::new(window).ok()?;
let mut surface = Surface::new(&context, window).ok()?;
surface.resize(width, height).ok()?;
let mut buffer = surface.buffer_mut().ok()?;
write_pixels(pixels, &mut buffer);
buffer.present().ok()?;
Some(())
}
}
impl ApplicationHandler for WindowApp {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let attrs = WindowAttributes::default()
.with_title("tauri-runtime-servocat demo")
.with_inner_size(PhysicalSize::new(
self.viewport.width(),
self.viewport.height(),
));
let _ = event_loop.create_window(attrs).map(|window| {
window.request_redraw();
let pixels = render_to_pixels_with(
&self.frame,
self.viewport.width(),
self.viewport.height(),
&mut self.text_renderer,
);
self.window = Some(window);
self.pixels = Some(pixels);
});
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
let _ = self.present();
}
ignored => {
let _ = ignored;
}
}
}
}
fn write_pixels(pixels: &PixelBuffer, buffer: &mut softbuffer::Buffer<'_, &Window, &Window>) {
pixels
.rgba()
.chunks_exact(4)
.map(rgba_to_softbuffer_word)
.zip(buffer.iter_mut())
.for_each(|(word, slot)| *slot = word);
}
fn rgba_to_softbuffer_word(chunk: &[u8]) -> u32 {
let red = chunk.first().copied().unwrap_or(0);
let green = chunk.get(1).copied().unwrap_or(0);
let blue = chunk.get(2).copied().unwrap_or(0);
let alpha = chunk.get(3).copied().unwrap_or(0);
let inv = 255_u8 - alpha;
let out_r = red.saturating_add(inv);
let out_g = green.saturating_add(inv);
let out_b = blue.saturating_add(inv);
(u32::from(out_r) << 16) | (u32::from(out_g) << 8) | u32::from(out_b)
}