pub mod blur;
pub mod canvas;
mod present;
pub mod shapes;
pub mod sprites;
use crate::core::Rgba;
use crate::gpu::atlas::{AtlasPair, TextureAtlas};
use crate::gpu::capture::CapturedFrame;
use crate::gpu::context::Error;
use crate::gpu::scene::{PrimitiveKind, Scene};
use crate::gpu::text::GlyphTarget;
use canvas::Canvas;
pub use present::{Presenter, SoftbufferPresenter};
use sprites::AtlasView;
fn view(atlas: &TextureAtlas) -> Option<AtlasView<'_>> {
atlas.cpu_pixels().map(|(pixels, size, channels)| AtlasView { pixels, size, channels })
}
pub struct CpuRenderer {
presenter: Option<Box<dyn Presenter>>,
width: u32,
height: u32,
pub(crate) atlases: AtlasPair,
}
impl CpuRenderer {
pub fn new(presenter: Option<Box<dyn Presenter>>, width: u32, height: u32) -> Self {
Self { presenter, width: width.max(1), height: height.max(1), atlases: AtlasPair::cpu() }
}
pub fn size(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 {
(self.width, self.height) = (width, height);
}
}
pub fn glyph_target(&mut self) -> GlyphTarget<'_> {
self.atlases.target(None)
}
pub fn take_atlas_overflow(&mut self) -> bool {
self.atlases.take_overflow()
}
pub fn shrink_caches(&mut self) {
self.atlases.clear();
}
pub fn draw(&self, pixels: &mut [u32], scene: &Scene, scale: f32, clear: Rgba) {
let mut canvas = Canvas { pixels, width: self.width, height: self.height, scale };
canvas.clear(clear);
let (mono, color) = (view(&self.atlases.monochrome), view(&self.atlases.polychrome));
for batch in &scene.batches {
let range = batch.range.start as usize..batch.range.end as usize;
let transform = scene.transforms.get(batch.transform as usize).copied().unwrap_or_default();
let moved = !transform.is_identity() && transform.is_axis_aligned();
match batch.kind {
PrimitiveKind::Blur => scene.blurs[range].iter().for_each(|b| blur::blur(&mut canvas, b)),
PrimitiveKind::Shadow => scene.shadows[range].iter().for_each(|s| match moved {
true => shapes::shadow(&mut canvas, &transform.shadow(s)),
false => shapes::shadow(&mut canvas, s),
}),
PrimitiveKind::Quad => scene.quads[range].iter().for_each(|q| match moved {
true => shapes::quad(&mut canvas, &transform.quad(q)),
false => shapes::quad(&mut canvas, q),
}),
PrimitiveKind::Underline => scene.underlines[range].iter().for_each(|u| match moved {
true => shapes::underline(&mut canvas, &transform.underline(u)),
false => shapes::underline(&mut canvas, u),
}),
PrimitiveKind::Monochrome => {
let Some(atlas) = &mono else { continue };
for sprite in &scene.monochrome_sprites[range] {
let sprite = if moved { transform.sprite(sprite) } else { *sprite };
sprites::sprite(&mut canvas, atlas, &sprite, false);
}
}
PrimitiveKind::Polychrome => {
let Some(atlas) = &color else { continue };
for sprite in &scene.polychrome_sprites[range] {
let sprite = if moved { transform.sprite(sprite) } else { *sprite };
sprites::sprite(&mut canvas, atlas, &sprite, true);
}
}
}
}
}
pub fn render(&mut self, scene: &Scene, scale: f32, clear: Rgba) -> Result<bool, Error> {
let Some(mut presenter) = self.presenter.take() else { return Ok(false) };
let (width, height) = (self.width, self.height);
let result = presenter.present(width, height, &mut |pixels| self.draw(pixels, scene, scale, clear));
self.presenter = Some(presenter);
result.map(|()| true)
}
pub fn capture(&mut self, scene: &Scene, scale: f32, clear: Rgba) -> CapturedFrame {
let mut pixels = vec![0u32; (self.width * self.height) as usize];
self.draw(&mut pixels, scene, scale, clear);
let rgba =
pixels.iter().flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, *pixel as u8, 255]).collect();
CapturedFrame { width: self.width, height: self.height, rgba }
}
}