summaryrefslogtreecommitdiff
path: root/src/export
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-12 10:35:42 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-12 10:35:42 +0100
commit2271d67f8f5fe65882e74622ad01c075102725b1 (patch)
tree8753166a1940ea75aed3371ccc4f463465b4e0f7 /src/export
parentc38d55614af0226be8eb3f3e1500da8b7be2fec8 (diff)
Faster image rendering
Diffstat (limited to 'src/export')
-rw-r--r--src/export/pdf/image.rs4
-rw-r--r--src/export/render.rs103
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 {