mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Faster image rendering
This commit is contained in:
parent
c38d55614a
commit
2271d67f8f
@ -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);
|
||||
|
@ -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,64 +317,6 @@ fn render_shape(
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Render a raster or SVG image into the canvas.
|
||||
fn render_image(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
image: &Image,
|
||||
size: Size,
|
||||
) -> Option<()> {
|
||||
let view_width = size.x.to_f32();
|
||||
let view_height = size.y.to_f32();
|
||||
|
||||
let aspect = (image.width() as f32) / (image.height() as f32);
|
||||
let scale = ts.sx.max(ts.sy);
|
||||
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 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(),
|
||||
sk::SpreadMode::Pad,
|
||||
sk::FilterQuality::Nearest,
|
||||
1.0,
|
||||
sk::Transform::from_scale(scale_x, scale_y),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let rect = sk::Rect::from_xywh(0.0, 0.0, view_width, view_height)?;
|
||||
canvas.fill_rect(rect, &paint, ts, mask);
|
||||
|
||||
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();
|
||||
@ -403,6 +346,70 @@ fn convert_path(path: &geom::Path) -> Option<sk::Path> {
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
/// Render a raster or SVG image into the canvas.
|
||||
fn render_image(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
image: &Image,
|
||||
size: Size,
|
||||
) -> Option<()> {
|
||||
let view_width = size.x.to_f32();
|
||||
let view_height = size.y.to_f32();
|
||||
|
||||
let aspect = (image.width() as f32) / (image.height() as f32);
|
||||
let scale = ts.sx.max(ts.sy);
|
||||
let w = (scale * view_width.max(aspect * view_height)).ceil() as u32;
|
||||
let h = ((w as f32) / aspect).ceil() as u32;
|
||||
|
||||
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(),
|
||||
sk::SpreadMode::Pad,
|
||||
sk::FilterQuality::Nearest,
|
||||
1.0,
|
||||
sk::Transform::from_scale(scale_x, scale_y),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let rect = sk::Rect::from_xywh(0.0, 0.0, view_width, view_height)?;
|
||||
canvas.fill_rect(rect, &paint, ts, mask);
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Some(Arc::new(pixmap))
|
||||
}
|
||||
|
||||
impl From<Transform> for sk::Transform {
|
||||
fn from(transform: Transform) -> Self {
|
||||
let Transform { sx, ky, kx, sy, tx, ty } = transform;
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Image handling.
|
||||
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::diag::{format_xml_like_error, StrResult};
|
||||
use crate::util::Buffer;
|
||||
@ -50,8 +51,9 @@ impl Image {
|
||||
}
|
||||
|
||||
/// Decode the image.
|
||||
pub fn decode(&self) -> StrResult<DecodedImage> {
|
||||
Ok(match self.format {
|
||||
#[comemo::memoize]
|
||||
pub fn decode(&self) -> StrResult<Arc<DecodedImage>> {
|
||||
Ok(Arc::new(match self.format {
|
||||
ImageFormat::Vector(VectorFormat::Svg) => {
|
||||
let opts = usvg::Options::default();
|
||||
let tree = usvg::Tree::from_data(&self.data, &opts.to_ref())
|
||||
@ -64,7 +66,7 @@ impl Image {
|
||||
let dynamic = reader.decode().map_err(format_image_error)?;
|
||||
DecodedImage::Raster(dynamic, format)
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user