mirror of
https://github.com/penpot/penpot.git
synced 2026-07-21 05:27:50 +00:00
- 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
58 lines
1.7 KiB
Rust
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;
|