diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-12 10:35:42 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-12 10:35:42 +0100 |
| commit | 2271d67f8f5fe65882e74622ad01c075102725b1 (patch) | |
| tree | 8753166a1940ea75aed3371ccc4f463465b4e0f7 /src/export | |
| parent | c38d55614af0226be8eb3f3e1500da8b7be2fec8 (diff) | |
Faster image rendering
Diffstat (limited to 'src/export')
| -rw-r--r-- | src/export/pdf/image.rs | 4 | ||||
| -rw-r--r-- | src/export/render.rs | 103 |
2 files changed, 57 insertions, 50 deletions
diff --git a/src/export/pdf/image.rs b/src/export/pdf/image.rs index e507a613..78731734 100644 --- a/src/export/pdf/image.rs +++ b/src/export/pdf/image.rs @@ -17,10 +17,10 @@ pub fn write_images(ctx: &mut PdfContext) { // Add the primary image. // TODO: Error if image could not be encoded. - match image.decode().unwrap() { + match image.decode().unwrap().as_ref() { DecodedImage::Raster(dynamic, format) => { // TODO: Error if image could not be encoded. - let (data, filter, has_color) = encode_image(format, &dynamic).unwrap(); + let (data, filter, has_color) = encode_image(*format, &dynamic).unwrap(); let mut image = ctx.writer.image_xobject(image_ref, &data); image.filter(filter); image.width(width as i32); diff --git a/src/export/render.rs b/src/export/render.rs index 68625e23..86648e96 100644 --- a/src/export/render.rs +++ b/src/export/render.rs @@ -1,6 +1,7 @@ //! Rendering into raster images. use std::io::Read; +use std::sync::Arc; use image::imageops::FilterType; use image::{GenericImageView, Rgba}; @@ -316,6 +317,35 @@ fn render_shape( Some(()) } +/// Convert a Typst path into a tiny-skia path. +fn convert_path(path: &geom::Path) -> Option<sk::Path> { + let mut builder = sk::PathBuilder::new(); + for elem in &path.0 { + match elem { + PathElement::MoveTo(p) => { + builder.move_to(p.x.to_f32(), p.y.to_f32()); + } + PathElement::LineTo(p) => { + builder.line_to(p.x.to_f32(), p.y.to_f32()); + } + PathElement::CubicTo(p1, p2, p3) => { + builder.cubic_to( + p1.x.to_f32(), + p1.y.to_f32(), + p2.x.to_f32(), + p2.y.to_f32(), + p3.x.to_f32(), + p3.y.to_f32(), + ); + } + PathElement::ClosePath => { + builder.close(); + } + }; + } + builder.finish() +} + /// Render a raster or SVG image into the canvas. fn render_image( canvas: &mut sk::Pixmap, @@ -332,34 +362,13 @@ fn render_image( let w = (scale * view_width.max(aspect * view_height)).ceil() as u32; let h = ((w as f32) / aspect).ceil() as u32; - let mut pixmap = sk::Pixmap::new(w, h)?; - match image.decode().unwrap() { - DecodedImage::Raster(dynamic, _) => { - let downscale = w < image.width(); - let filter = - if downscale { FilterType::Lanczos3 } else { FilterType::CatmullRom }; - let buf = dynamic.resize(w, h, filter); - for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { - let Rgba([r, g, b, a]) = src; - *dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply(); - } - } - DecodedImage::Svg(tree) => { - resvg::render( - &tree, - FitTo::Size(w, h), - sk::Transform::identity(), - pixmap.as_mut(), - )?; - } - } - + let pixmap = scaled_texture(image, w, h)?; let scale_x = view_width / pixmap.width() as f32; let scale_y = view_height / pixmap.height() as f32; let paint = sk::Paint { shader: sk::Pattern::new( - pixmap.as_ref(), + (*pixmap).as_ref(), sk::SpreadMode::Pad, sk::FilterQuality::Nearest, 1.0, @@ -374,33 +383,31 @@ fn render_image( Some(()) } -/// Convert a Typst path into a tiny-skia path. -fn convert_path(path: &geom::Path) -> Option<sk::Path> { - let mut builder = sk::PathBuilder::new(); - for elem in &path.0 { - match elem { - PathElement::MoveTo(p) => { - builder.move_to(p.x.to_f32(), p.y.to_f32()); - } - PathElement::LineTo(p) => { - builder.line_to(p.x.to_f32(), p.y.to_f32()); - } - PathElement::CubicTo(p1, p2, p3) => { - builder.cubic_to( - p1.x.to_f32(), - p1.y.to_f32(), - p2.x.to_f32(), - p2.y.to_f32(), - p3.x.to_f32(), - p3.y.to_f32(), - ); - } - PathElement::ClosePath => { - builder.close(); +/// Prepare a texture for an image at a scaled size. +#[comemo::memoize] +fn scaled_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> { + let mut pixmap = sk::Pixmap::new(w, h)?; + match image.decode().unwrap().as_ref() { + DecodedImage::Raster(dynamic, _) => { + let downscale = w < image.width(); + let filter = + if downscale { FilterType::Lanczos3 } else { FilterType::CatmullRom }; + let buf = dynamic.resize(w, h, filter); + for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { + let Rgba([r, g, b, a]) = src; + *dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply(); } - }; + } + DecodedImage::Svg(tree) => { + resvg::render( + &tree, + FitTo::Size(w, h), + sk::Transform::identity(), + pixmap.as_mut(), + )?; + } } - builder.finish() + Some(Arc::new(pixmap)) } impl From<Transform> for sk::Transform { |
