Elena Torro 48cec4c83b Add headless (GPU-free) render-wasm export path
- init_headless + RenderState::try_new_headless / Surfaces::try_new_headless:
  boot on CPU-raster placeholders, no GL context
- render_shape_raster + render/raster.rs: CPU raster PNG export
- CanvasCtx: GPU-free context shared by the PDF and raster canvas paths
- get_fonts_for_shape + Shape/TextContent font enumeration
- clear_fonts; ImageStore::without_gpu
- _build_env: ENVIRONMENT=web,node so the module loads under Node
2026-06-17 10:50:58 +02:00

58 lines
1.7 KiB
Rust

use skia_safe as skia;
use crate::error::{Error, Result};
use crate::state::ShapesPoolRef;
use crate::uuid::Uuid;
use super::vector::{self, VectorTarget};
use super::RenderState;
/// Renders a shape tree to PNG bytes on a CPU raster surface (no GPU/WebGL),
/// through the same `render_tree` used by the PDF backend. Returns
/// `(png_bytes, width_px, height_px)`. With a headless `RenderState` this runs
/// without a GL context.
pub fn render_to_raster(
shared: &mut RenderState,
id: &Uuid,
tree: ShapesPoolRef,
scale: f32,
) -> Result<(Vec<u8>, i32, i32)> {
let Some(shape) = tree.get(id) else {
return Ok((Vec::new(), 0, 0));
};
let bounds = shape.extrect(tree, scale);
let width = (bounds.width() * scale).ceil() as i32;
let height = (bounds.height() * scale).ceil() as i32;
if width <= 0 || height <= 0 {
return Ok((Vec::new(), 0, 0));
}
let mut surface = skia::surfaces::raster_n32_premul((width, height)).ok_or_else(|| {
Error::CriticalError("Failed to create raster export surface".to_string())
})?;
{
let canvas = surface.canvas();
canvas.clear(skia::Color::TRANSPARENT);
canvas.scale((scale, scale));
canvas.translate((-bounds.left(), -bounds.top()));
vector::render_tree(shared, canvas, id, tree, scale, VectorTarget::Raster)?;
}
let data = surface
.image_snapshot()
.encode(
None::<&mut skia::gpu::DirectContext>,
skia::EncodedImageFormat::PNG,
100,
)
.ok_or_else(|| Error::CriticalError("PNG encode failed".to_string()))?;
Ok((data.as_bytes().to_vec(), width, height))
}
#[cfg(all(test, not(target_arch = "wasm32")))]
#[path = "raster_tests.rs"]
mod tests;