mirror of
https://github.com/typst/typst
synced 2025-08-10 13:17:55 +08:00
Refactor a bit more
This commit is contained in:
parent
9362c0e63e
commit
cd2a61c354
@ -41,11 +41,11 @@ pub fn layout_image(
|
||||
|
||||
if has_foreign_object {
|
||||
engine.sink.warn(warning!(
|
||||
span,
|
||||
"image contains foreign object";
|
||||
hint: "SVG images with foreign objects might render incorrectly in typst";
|
||||
hint: "see https://github.com/typst/typst/issues/1421 for more information"
|
||||
));
|
||||
span,
|
||||
"image contains foreign object";
|
||||
hint: "SVG images with foreign objects might render incorrectly in typst";
|
||||
hint: "see https://github.com/typst/typst/issues/1421 for more information"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ pub use self::raster::{RasterFormat, RasterImage};
|
||||
pub use self::svg::SvgImage;
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
|
||||
use comemo::Tracked;
|
||||
|
@ -67,9 +67,9 @@ impl PixmapImage {
|
||||
&self.0.source.data
|
||||
}
|
||||
|
||||
/// Transform the image data into a [`DynamicImage`].
|
||||
/// Transform the image data to a [`DynamicImage`].
|
||||
#[comemo::memoize]
|
||||
pub fn to_image(&self) -> Arc<DynamicImage> {
|
||||
pub fn to_dynamic(&self) -> Arc<DynamicImage> {
|
||||
// TODO: Optimize by returning a `View` if possible?
|
||||
fn decode<P: Pixel<Subpixel = u8>>(
|
||||
source: &PixmapSource,
|
||||
|
@ -130,14 +130,14 @@ pub fn deferred_image(
|
||||
Some(to_color_space(raster.dynamic().color()))
|
||||
}
|
||||
ImageKind::Pixmap(pixmap) if pixmap.icc_profile().is_none() => {
|
||||
Some(to_color_space(pixmap.to_image().color()))
|
||||
Some(to_color_space(pixmap.to_dynamic().color()))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// PDF/A does not appear to allow interpolation[^1].
|
||||
// [^1]: https://github.com/typst/typst/issues/2942
|
||||
let interpolate = image.scaling() == Smart::Custom(ImageScaling::Smooth) && !pdfa;
|
||||
// PDF/A does not appear to allow interpolation.
|
||||
// See https://github.com/typst/typst/issues/2942.
|
||||
let interpolate = !pdfa && image.scaling() == Smart::Custom(ImageScaling::Smooth);
|
||||
|
||||
let deferred = Deferred::new(move || match image.kind() {
|
||||
ImageKind::Raster(raster) => {
|
||||
@ -159,7 +159,7 @@ pub fn deferred_image(
|
||||
Ok(EncodedImage::Svg(chunk, id))
|
||||
}
|
||||
ImageKind::Pixmap(pixmap) => Ok(encode_raster_image(
|
||||
&pixmap.to_image(),
|
||||
&pixmap.to_dynamic(),
|
||||
pixmap.icc_profile(),
|
||||
EncodeFormat::Flate,
|
||||
interpolate,
|
||||
@ -215,16 +215,6 @@ fn encode_raster_image(
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches an [`image::ColorType`] to [`ColorSpace`].
|
||||
fn to_color_space(color: image::ColorType) -> ColorSpace {
|
||||
use image::ColorType::*;
|
||||
match color {
|
||||
L8 | La8 | L16 | La16 => ColorSpace::D65Gray,
|
||||
Rgb8 | Rgba8 | Rgb16 | Rgba16 | Rgb32F | Rgba32F => ColorSpace::Srgb,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an image's alpha channel if present.
|
||||
#[typst_macros::time(name = "encode alpha")]
|
||||
fn encode_alpha(image: &DynamicImage) -> (Vec<u8>, Filter) {
|
||||
@ -282,3 +272,13 @@ enum EncodeFormat {
|
||||
DctDecode,
|
||||
Flate,
|
||||
}
|
||||
|
||||
/// Matches an [`image::ColorType`] to [`ColorSpace`].
|
||||
fn to_color_space(color: image::ColorType) -> ColorSpace {
|
||||
use image::ColorType::*;
|
||||
match color {
|
||||
L8 | La8 | L16 | La16 => ColorSpace::D65Gray,
|
||||
Rgb8 | Rgba8 | Rgb16 | Rgba16 | Rgb32F | Rgba32F => ColorSpace::Srgb,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -59,34 +59,35 @@ pub fn render_image(
|
||||
/// Prepare a texture for an image at a scaled size.
|
||||
#[comemo::memoize]
|
||||
fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
||||
let mut texture = sk::Pixmap::new(w, h)?;
|
||||
match image.kind() {
|
||||
ImageKind::Raster(raster) => scale_image(raster.dynamic(), image.scaling(), w, h),
|
||||
ImageKind::Pixmap(raster) => {
|
||||
scale_image(&raster.to_image(), image.scaling(), w, h)
|
||||
ImageKind::Raster(raster) => {
|
||||
scale_image(&mut texture, raster.dynamic(), image.scaling())
|
||||
}
|
||||
ImageKind::Pixmap(pixmap) => {
|
||||
scale_image(&mut texture, &pixmap.to_dynamic(), image.scaling())
|
||||
}
|
||||
// Safety: We do not keep any references to tree nodes beyond the scope
|
||||
// of `with`.
|
||||
ImageKind::Svg(svg) => {
|
||||
let mut pixmap = sk::Pixmap::new(w, h)?;
|
||||
let tree = svg.tree();
|
||||
let ts = tiny_skia::Transform::from_scale(
|
||||
w as f32 / tree.size().width(),
|
||||
h as f32 / tree.size().height(),
|
||||
);
|
||||
resvg::render(tree, ts, &mut pixmap.as_mut());
|
||||
Some(Arc::new(pixmap))
|
||||
resvg::render(tree, ts, &mut texture.as_mut());
|
||||
}
|
||||
}
|
||||
Some(Arc::new(texture))
|
||||
}
|
||||
|
||||
/// Scale a rastered image to a given size and return texture.
|
||||
/// Scale a rastered image to a given size and write it into the `texture`.
|
||||
fn scale_image(
|
||||
texture: &mut sk::Pixmap,
|
||||
image: &image::DynamicImage,
|
||||
scaling: Smart<ImageScaling>,
|
||||
w: u32,
|
||||
h: u32,
|
||||
) -> Option<Arc<sk::Pixmap>> {
|
||||
let mut pixmap = sk::Pixmap::new(w, h)?;
|
||||
) {
|
||||
let w = texture.width();
|
||||
let h = texture.height();
|
||||
|
||||
let buf;
|
||||
let resized = if (w, h) == (image.width(), image.height()) {
|
||||
// Small optimization to not allocate in case image is not resized.
|
||||
@ -94,18 +95,16 @@ fn scale_image(
|
||||
} else {
|
||||
let upscale = w > image.width();
|
||||
let filter = match scaling {
|
||||
Smart::Auto | Smart::Custom(ImageScaling::Smooth) if upscale => {
|
||||
FilterType::CatmullRom
|
||||
}
|
||||
Smart::Custom(ImageScaling::Pixelated) => FilterType::Nearest,
|
||||
_ if upscale => FilterType::CatmullRom,
|
||||
_ => FilterType::Lanczos3, // downscale
|
||||
};
|
||||
buf = image.resize_exact(w, h, filter);
|
||||
&buf
|
||||
};
|
||||
for ((_, _, src), dest) in resized.pixels().zip(pixmap.pixels_mut()) {
|
||||
|
||||
for ((_, _, src), dest) in resized.pixels().zip(texture.pixels_mut()) {
|
||||
let Rgba([r, g, b, a]) = src;
|
||||
*dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply();
|
||||
}
|
||||
Some(Arc::new(pixmap))
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
use std::io::Cursor;
|
||||
|
||||
use base64::Engine;
|
||||
use ecow::{eco_format, EcoString};
|
||||
use image::error::UnsupportedError;
|
||||
use image::{codecs::png::PngEncoder, ImageEncoder};
|
||||
use typst_library::foundations::Smart;
|
||||
use typst_library::layout::{Abs, Axes};
|
||||
@ -24,8 +21,8 @@ impl SVGRenderer {
|
||||
match image.scaling() {
|
||||
Smart::Auto => {}
|
||||
Smart::Custom(ImageScaling::Smooth) => {
|
||||
// This is still experimental and not implemented in all major browsers[^1].
|
||||
// [^1]: https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering#browser_compatibility
|
||||
// This is still experimental and not implemented in all major browsers.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering#browser_compatibility
|
||||
self.xml.write_attribute("style", "image-rendering: smooth")
|
||||
}
|
||||
Smart::Custom(ImageScaling::Pixelated) => {
|
||||
@ -51,20 +48,19 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
||||
},
|
||||
ImageFormat::Pixmap(_) => "png",
|
||||
};
|
||||
let data_owned;
|
||||
|
||||
let mut buf;
|
||||
let data = match image.kind() {
|
||||
ImageKind::Raster(raster) => raster.data(),
|
||||
ImageKind::Svg(svg) => svg.data(),
|
||||
ImageKind::Pixmap(pixmap) => {
|
||||
let mut data = Cursor::new(vec![]);
|
||||
let mut encoder = PngEncoder::new(&mut data);
|
||||
buf = vec![];
|
||||
let mut encoder = PngEncoder::new(&mut buf);
|
||||
if let Some(icc_profile) = pixmap.icc_profile() {
|
||||
let _: Result<(), UnsupportedError> =
|
||||
encoder.set_icc_profile(icc_profile.to_vec());
|
||||
encoder.set_icc_profile(icc_profile.to_vec()).ok();
|
||||
}
|
||||
pixmap.to_image().write_with_encoder(encoder).unwrap();
|
||||
data_owned = data.into_inner();
|
||||
&*data_owned
|
||||
pixmap.to_dynamic().write_with_encoder(encoder).unwrap();
|
||||
buf.as_slice()
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user